Entrevista en C++ terminando la serie 1

1.strcpy()

char * strcpy( char *strDest, const char *strSrc ) 
{
    
    
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest; 
 while( (*strDest++ = * strSrc++) != ‘\0); 
 return address;
}

2. ¿Qué hay de malo en el siguiente código?

void GetMemory( char **p, int num )
{
    
    
 *p = (char *) malloc( num );
}
void Test( void )
{
    
    
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, "hello" ); 
 printf( str ); 
}
1.*p没有判断NULL 的情况
2.没有free
3.printf没有字符输出格式化
void Test( void )
{
    
    
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str ); 
 ... //省略的其它语句
}
1.没有判断*str=NULL
2.*str释放以后没有等于NULL 

3. Escriba una función para mover una cadena de caracteres compuesta de char hacia la derecha n veces. Por ejemplo, originalmente era "abcdefghi" y si n=2, debería ser "hiabcdefg" después del cambio. El encabezado de la función es así:
//pStr es un puntero a una cadena que termina en '\0'
//los pasos son el n requerido para moverse

void LoopMove(char *str,int steps){
    
    
	int len = strlen(str);
	char tmp[MAXSIZE];
	strcpy(tmp,str+len-steps);
	strcpy(tmp+steps,str);
	*(tmp+len) = '/0';
	strcpy(str,tmp);
}
void LoopMove(char *str,int steps){
    
    
	int len = strlen(str);
	char tmp[MAXSIZE];
	memcpy(tmp,str+len-steps,steps);
	memcpy(str+steps,str,len-steps);
	memcpy(str,tmp,steps);
}

4. Escriba el constructor, el destructor y la función de asignación de la cadena.

class String{
    
    
public:
	String(const char *str = NULL);//普通构造函数
	String(const String &other);//拷贝构造函数
	~String(void);//析构函数
	String &operator =(const String &other);//赋值函数
private:
	char *m_data;//用于保存字符串
}
//普通构造函数
String::String(const char *str){
    
    
	if(str == NULL){
    
    
		m_data = new char[1];
		*m_data = '\0';
	}else{
    
    
		int length = strlen(str);
		m_data = new char[length+1];
		strcpy(m_data,str);
	}
}
//普通析构函数
String::~String(void){
    
    
 delete [] m_data;
}
//拷贝构造函数
String::String(const String &other){
    
    
	int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
}
//赋值函数
String &String::operator =(const String &other){
    
    
	if(this == &other)
		return *this;
	delete [] m_data;
	int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
	return *this;
}

5. El papel de la estática y la constante.

  • Estático

(1) El alcance de la función de la variable estática en el cuerpo de la función es el cuerpo de la función. A diferencia de la variable automática, la memoria de esta variable solo se asigna una vez, por lo que su valor aún mantendrá el último valor cuando se llame la próxima vez; (2) En el
módulo, las funciones utilizadas en el módulo pueden acceder a las variables globales estáticas, pero no pueden acceder otras funciones fuera del módulo;
(3) La función estática en el módulo solo puede ser llamada por otras funciones en este módulo, y el alcance de uso de esta función se limita a la declaración en su módulo;
(4) Las variables miembro estáticas de la clase pertenecen a toda la clase y solo hay una copia de todos los objetos de la clase; (
5) El Las funciones miembro estáticas de la clase pertenecen a toda la clase y esta función no recibe este puntero, por lo que solo puede acceder a las variables miembro estáticas de la clase.

  • constante

(1) Para evitar que se cambie una variable, puede utilizar la palabra clave const. Al definir la variable constante, generalmente es necesario inicializarla, porque no habrá posibilidad de cambiarla en el futuro; >(2) Para los punteros, puede especificar el puntero en sí como constante o especificar los datos a los que apunta el puntero como const, o ambos se designan como const al mismo tiempo;
(3) En una declaración de función, const puede modificar el parámetro formal, indicando que es un parámetro de entrada y su valor no se puede cambiar dentro de la función; ( 4)
Para una función miembro de una clase, si especificarla como un tipo constante indica que es una función constante y no puede modificar las variables miembro de la clase; (5)
Para funciones miembro de una clase, a veces es necesario especificar su valor de retorno como un tipo constante para que su valor de retorno no sea "valor izquierdo"

6. Cómo utilizar una función para juzgar big endian o small endian, devolver 1 si es big endian y devolver 0 si es big endian

#include <iostream>
int checkCPU(){
    
    
union w{
    
    
	int a;
	char b;
} c;
c.a = 1;
return(c.b == 1);
}

7. La diferencia entre C y C++

  • Ideológicamente: C++ es un lenguaje orientado a objetos, mientras que c es un lenguaje de programación estructurado orientado a procesos.
  • Gramaticalmente: C ++ tiene tres características de encapsulación, herencia y polimorfismo.
    En comparación con C, C ++ agrega muchas funciones de seguridad, como la conversión de tipos obligatoria.
    C ++ admite la programación de paradigmas, como clases de plantillas, plantillas de funciones, etc.

8. Cuatro conversiones de conversión en c++

  • const_cast: usado para convertir una variable constante en una variable no constante
  • static_cast: se usa para varias conversiones implícitas, como no constante a constante, void* a puntero, etc. static_cast se puede usar para conversión polimórfica ascendente pero no descendente
  • dynamic_cast: conversión de tipo dinámico. Solo se puede usar para clases con funciones virtuales, se usa para la conversión entre jerarquías de clases y solo puede transferir punteros o referencias. Los punteros ilegales devuelven NULL y las referencias ilegales arrojan excepciones.
  • reinterpret_cast: cualquier cosa se puede convertir

9. La diferencia entre punteros y referencias.

  • Los punteros tienen espacio de almacenamiento independiente, espacio de almacenamiento compartido de referencia, la referencia es solo un alias
  • Los punteros se pueden inicializar en NULL, mientras que las referencias son del tamaño del objeto al que se hace referencia.
  • sizeof() se refiere al tamaño del objeto, mientras que el puntero es la dirección 4/8
  • Puede tener punteros constantes pero no referencias constantes.
  • Los punteros pueden tener varios niveles de punteros, mientras que las referencias solo pueden tener un nivel.
  • El puntero ++ y la referencia ++ tienen significados diferentes
  • Los punteros pueden apuntar a otros objetos, mientras que las referencias no se pueden cambiar.
  • Al pasar parámetros, es necesario desreferenciar el puntero para operar en el objeto, y la modificación directa de la referencia cambiará el objeto al que apunta la referencia.
  • Si devuelve un objeto o memoria asignada por memoria dinámica, debe usar un puntero y la referencia puede provocar una pérdida de memoria.

10 Comprensión de cuatro punteros inteligentes en C ++: Shared_ptr, Unique_ptr, débil_ptr, auto_ptr

Por qué utilizar un puntero inteligente: la función de un puntero inteligente es administrar un puntero, porque a veces se olvida liberar el espacio de memoria solicitado, lo que resulta en una pérdida de memoria. El uso de punteros inteligentes puede evitar en gran medida que sucedan tales cosas, porque un puntero inteligente es una clase. Cuando sale del alcance, la clase llamará automáticamente al destructor y liberará automáticamente el espacio de memoria sin liberar el espacio manualmente.

 - auto_ptr:采用所有权模式
#include <iostream>
unique_ptr<string> p1(new string("auto"));
unique_ptr<string> p2;
p2 = p1;//不会报错
//p2剥夺p1的运行权,当访问p1的时候会报错,auto_ptr的缺点是存在潜在崩溃 的情况

  • Unique_ptr: garantiza que solo un puntero inteligente pueda apuntar al objeto al mismo tiempo. Es especialmente útil para evitar fugas de recursos (por ejemplo, "olvidó llamar a eliminar debido a una excepción después de crear un objeto con nuevo").
#include <iostream>
unique_ptr<string> p1(new string("auto"));
unique_ptr<string> p2;
p2 = p1;//禁止如果强制需要的话就用 p2 = move(p1);
p2 = unique_ptr<string> new string("I love you");
  • shared_ptr: implementa el concepto de propiedad compartida. Varios punteros pueden apuntar al mismo objeto y el objeto y los recursos asociados se liberan cuando se destruye la última referencia. Utiliza un mecanismo de conteo para contar el mismo recurso compartido por varios punteros, verificar el número de propietarios de recursos mediante use_count(), construirlo mediante new y construirlo pasando auto_ptr, Unique_ptr y débil_ptr. Cuando llamamos a liberar, el puntero actual libera la propiedad del recurso:
成员函数:
	use_count();//返回计数的个数
	swap();//交换两个shared_ptr对象(交换所拥有的对象)
	reset();//放弃内部对象的所有权或拥有对象的变更,计数减少
	get();//返回内部指针 如:shared_ptr<int>sp(new int(1))此时sp等价sp.get()
  • débil_ptr: un puntero inteligente que no controla el ciclo de vida. Apunta a un objeto administrado por share_ptr. La gestión de la memoria del objeto es el share_ptr fuertemente referenciado.weak_ptr es solo un medio para acceder al objeto. El propósito del diseño de débil_ptr es cooperar con un puntero inteligente al que hace referencia compartido_ptr para ayudar a compartido_ptr. Solo se puede construir a partir de un compartido_ptr u otro débil_ptr. Su construcción y destrucción no provocan que el recuento de referencia se incremente o disminuya. Weak_ptr se utiliza para resolver el problema de interbloqueo de la referencia mutua de Shared_ptr. Si dos Shared_ptr pueden referirse entre sí, entonces el recuento de referencias de estos dos punteros nunca puede caer a 0 y nunca se liberará. Es una referencia débil al objeto, no aumentará el número de referencias al objeto y convertirá entre share_ptr. Shared_ptr se le puede asignar directamente y puede obtener Shared_ptr llamando a la función de bloqueo.
class B;
class A
{
    
    
public:
shared_ptr<B> pb_;
~A()
{
    
    
cout<<"A delete\n";
}
};
class B
{
    
    
public:
shared_ptr<A> pa_;
~B()
{
    
    
cout<<"B delete\n";
}
};
void fun()
{
    
    
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
    
    
fun();
return 0;
}

11. La diferencia entre matriz y puntero.

puntero formación
La dirección donde se guardan los datos. Guarde el valor específico de los datos.
Generalmente se usa para estructuras de datos dinámicas. Generalmente se usa para una cantidad fija de datos y tipo de datos.
Asignar memoria a través de malloc Asignación y eliminación implícitas
Para acceder a los datos, primero obtenga el contenido del puntero y acceda a los datos a través de la dirección, lo cual es menos eficiente acceso directo a los datos

12. ¿Qué es un puntero salvaje?
Un puntero salvaje es un puntero a un objeto eliminado o a un área restringida de acceso no aplicada.

13. ¿Por qué el destructor debe ser una función virtual? ¿Por qué el destructor predeterminado de C++ no es una función virtual?

  • Configurar el destructor de la clase principal que puede heredarse como una función virtual puede garantizar que cuando creamos una nueva subclase y luego usamos el puntero de la clase base para apuntar al objeto, el espacio de la subclase se pueda liberar cuando el puntero de la clase base esté liberado para evitar pérdidas de memoria.
  • El destructor predeterminado de C++ no es una función virtual porque la función virtual requiere una tabla de funciones virtuales adicional y un puntero de tabla virtual, lo que ocupa memoria adicional. Para una clase que no se heredará, si su destructor es una función virtual, desperdiciará memoria. Por lo tanto, el destructor predeterminado de C++ no es una función virtual, sino que solo es necesario cuando necesita ser una clase principal.

14. Puntero de función, función de puntero

  1. Definición
    Un puntero de función es una variable de puntero que apunta a una función,
    una función de puntero lo es y el valor de retorno de una función es un puntero
  2. Propósito
    Puntero de función: función de devolución de llamada
  3. caso
char *fun(char *p){
    
    ...}//函数fun
char *(*pf)(char *p);//函数指针pf
pf = fun ;//函数指针指向fun
pf(p);//通过函数指针调用函数fun

15. La diferencia entre mapa y set.

Tanto el mapa como el conjunto son contenedores asociativos de C ++, y sus implementaciones subyacentes son árboles rojo-negro (RB-Tree). Debido a las diversas interfaces de operación abiertas por el mapa y el conjunto, RB-tree también las proporciona, por lo que casi todos los mapas y conjuntos El comportamiento de operación de set es transferir el comportamiento de operación de RB_tree
(1) Los elementos del mapa son pares clave-valor (palabra clave-valor): las palabras clave desempeñan el papel de índice, y lo opuesto a set es una simple colección de palabras clave. Cada elemento del conjunto contiene solo una palabra clave
(2) El iterador del conjunto es constante y no se permite modificar el valor del elemento. El mapa permite modificar el valor, pero no se permite la clave modificado. La razón es que el mapa y el conjunto se ordenan según las palabras clave para garantizar su orden. Si modifica la clave, primero debe eliminar la clave, luego ajustar el saldo y luego insertar el valor de la clave modificada para ajustar el saldo. La estructura del mapa y el conjunto se destruye, lo que provoca que el iterador falle. No sé si debería apuntar a la posición antes del cambio o a la posición después del cambio, así que configure el iterador del conjunto en STL en constante, y no se permite modificar el valor del iterador; y el iterador en el mapa no se permite modificar el valor de la clave
(3) el mapa admite la operación de la siguiente tabla, set no admite la operación de subíndice, el mapa puede use la clave como subíndice, y el operador de subíndice [] del mapa usa el código de clave como subíndice para realizar una búsqueda e insértelo si la clave no existe.

16. Introducir la asignación de STL.

El asignador STL se utiliza para encapsular los detalles de la gestión de memoria de los contenedores STL. En C++, su configuración y liberación de memoria son las siguientes: La nueva operación se divide en dos etapas: (1) llamar a ::operator
new para configurar la memoria (2) llamar al constructor del objeto para construir el contenido del objeto. La operación de eliminación se divide en dos partes: (1) llamar al objeto El destructor (2) llamar a ::operator
eliminar para liberar la función. Para una división precisa del trabajo, el
asignador STL distingue las dos fases de las operaciones: la asignación de memoria está a cargo de alloc::allocate (); la construcción de objetos está a cargo de ::construct(), y la destrucción de objetos está a cargo de ::destroy.
Al mismo tiempo, para mejorar la eficiencia de la gestión de la memoria y reducir la fragmentación de la memoria causada por la aplicación de la memoria, SGI
STL utiliza un configurador de dos niveles. Cuando el espacio asignado supera los 128 g, se utiliza el primer nivel, malloc().realloc (), función free () Asignación y liberación de espacio de memoria, mientras que el configurador de espacio de segundo nivel utiliza tecnología de grupo de memoria para administrar la memoria a través de listas vinculadas libres

17. La diferencia entre Struct y clases.

En C++, las clases se pueden definir con struct y class, las cuales se pueden heredar. La diferencia es: el permiso de herencia predeterminado y el permiso de acceso predeterminado de la estructura son públicos, mientras que el permiso de herencia predeterminado y el permiso de acceso predeterminado de la clase son privados.
Además, la clase también puede definir parámetros de clase de plantilla, como la plantilla <clase T, int i>.

18. Cómo juzgar si hay una pérdida de memoria

  • Utilice la herramienta de verificación de fugas de memoria Valgrind en Linux. Por otro lado, al escribir código, puede agregar la función estadística de la aplicación y la versión de la aldea, y contar si la aplicación actual y la memoria de liberación son consistentes, para juzgar si hay una fuga

19. ¿Por qué ocurre una falla de segmentación?

Los fallos de segmentación suelen ocurrir al acceder a direcciones de memoria ilegales:
1. Usar un puntero salvaje
2. Intentar modificar el contenido de una constante de cadena

20. ¿El principio de malloc y cuáles son las funciones de llamar a la llamada al sistema brk y a la llamada al sistema mmap?
La función Malloc se utiliza para asignar memoria dinámicamente. Para reducir la sobrecarga de la fragmentación de la memoria y las llamadas al sistema, malloc adopta el método de grupo de memoria, primero solicita un bloque grande de memoria como área del montón y luego divide el área del montón en varios bloques pequeños y usa bloques como Unidad básica de gestión de memoria. Cuando el usuario solicita memoria, se asigna directamente un bloque libre adecuado desde el montón. Malloc utiliza una estructura de lista vinculada implícita para dividir el área del montón en bloques continuos de diferentes tamaños, incluidos bloques asignados y bloques no asignados. Al mismo tiempo, malloc utiliza una estructura de lista vinculada explícita para administrar todos los bloques libres, es decir, dos La forma en que la lista enlazada se utiliza para conectar bloques libres. Juntos, cada bloque registra una dirección contigua y no asignada.
Al realizar la asignación de memoria, malloc atravesará todos los bloques libres a través de la lista vinculada implícita y seleccionará los bloques que cumplan con los requisitos de asignación para decidir si realizar la fusión de bloques.
Cuando se usa Malloc para solicitar memoria, generalmente llama a través del sistema brk o mmap para solicitarla. Entre ellos, cuando la memoria solicitada es inferior a 128 K, se utilizará la función del sistema brk para asignar en el montón; cuando la memoria solicitada es superior a 128 K, se utilizará la función del sistema mmap para asignar en el área de mapeo.
21. ¿Cómo se ve la gestión de memoria en C++?

En C++, la memoria virtual se divide en segmento de código, segmento de datos, BSS, área de montón, área de mapeo de archivos y área de pila.
Segmento de código: incluye un área de almacenamiento de solo lectura y un área de almacenamiento de texto, donde el área de almacenamiento de solo lectura almacena constantes de cadena y el área de almacenamiento de texto almacena el código de máquina del programa. Segmento de datos: almacena variables globales inicializadas y variables estáticas. Segmento bss: almacena globales no
inicializados
. variables y variables estáticas (local + global), y todas las variables globales y variables estáticas inicializadas en 0
Área del montón: asigne memoria dinámicamente en el área del montón cuando se llama a la función new/malloc, y se requieren eliminar y liberar para liberar la memoria manualmente
pila: parámetro de función variable local, el proceso de creación de Linux se establece con ulimit
Área de mapeo: biblioteca de vínculos dinámicos y llamada a la función mmap para mapeo de archivos

22. Explica la pérdida de memoria.

  • Concepto: la pérdida de memoria se refiere a la aplicación de memoria que ya no se usa pero no se libera.
  • pérdida de memoria del montón
  • Fuga de recursos del sistema: se refiere principalmente a que el programa utiliza los recursos asignados por el sistema, como Bitmap, handle, SOCKET, etc., pero no utiliza las funciones correspondientes para liberarlos, lo que provoca un grave desperdicio de recursos del sistema. lo que puede reducir seriamente el rendimiento del sistema y hacerlo inestable.
  • El destructor de la clase base no está configurado como función virtual. Subclases heredadas, no se llama a ningún destructor y, por lo tanto, no se produce ninguna desasignación.

23. La diferencia entre New y Malloc
1. new es un operador que no se puede sobrecargar, malloc es una función de biblioteca
2. la nueva excepción arroja bad_malloc, malloc arroja NULL
3. la asignación de malloc debe realizarse de acuerdo con el tamaño especificado, mientras que la nueva asignación es Es el tipo especificado
4. new devuelve el puntero del tipo especificado, mientras que malloc devuelve void*, por lo que malloc generalmente necesita realizar una conversión de tipo.

24. Nuevas características de c++11

1) Palabras clave y nueva sintaxis: auto, nullptr, for
2) Contenedores STL: std::array, std::forward_list, std:unordered_map, std::unordered_set
3) Multiproceso: std::thread, std::atomic, std ::consition_variable
4) Gestión de memoria del puntero inteligente: std::shared_ptr, std::weak_ptr
5) Otro: expresión lamda

Supongo que te gusta

Origin blog.csdn.net/weixin_40178954/article/details/102750425
Recomendado
Clasificación