¿Cómo se inicializan los miembros de la clase C ++ si no lo hago explícitamente?

Supongamos que tengo una clase con memebers privados ptr , name , pname , rname , crname y age . ¿Qué pasa si no los inicializo yo mismo? Aquí hay un ejemplo:

 class Example { private: int *ptr; string name; string *pname; string &rname; const string &crname; int age; public: Example() {} }; 

Y luego lo hago:

 int main() { Example ex; } 

¿Cómo se inicializan los miembros en ex? ¿Qué pasa con los punteros? ¿Los intérpretes de string e int obtienen 0-intialized con los constructores por defecto string() e int() ? ¿Qué pasa con el miembro de referencia? Además, ¿qué pasa con las referencias de const?

¿Qué más debería saber?

¿Alguien sabe un tutorial que cubre estos casos? Quizás en algunos libros? Tengo acceso en la biblioteca de la universidad a muchos libros en C ++.

Me gustaría aprenderlo para poder escribir mejores progtwigs (sin errores). ¡Cualquier comentario ayudaría!

En lugar de una inicialización explícita, la inicialización de miembros en clases funciona de forma idéntica a la inicialización de variables locales en funciones.

Para los objetos , se llama a su constructor predeterminado. Por ejemplo, para std::string , el constructor predeterminado lo establece en una cadena vacía. Si la clase del objeto no tiene un constructor predeterminado, será un error de comstackción si no lo inicializa explícitamente.

Para los tipos primitivos (punteros, ints, etc.), no se inicializan; contienen cualquier basura arbitraria que haya estado anteriormente en esa ubicación de memoria.

Para las referencias (por ejemplo, std::string& ), es ilegal no inicializarlas, y su comstackdor se quejará y se negará a comstackr dicho código. Las referencias siempre deben ser inicializadas.

Entonces, en su caso específico, si no se inicializan explícitamente:

  int *ptr; // Contains junk string name; // Empty string string *pname; // Contains junk string &rname; // Compile error const string &crname; // Compile error int age; // Contains junk 

Primero, déjame explicarte qué es una lista de mem-initializer . Una lista de inicializadores de mem es una lista de mem-initializer s separados por comas, donde cada mem-initializer es un nombre de miembro seguido de ( , seguido de una lista de expresiones , seguida de a ) . La lista de expresiones es cómo se construye el miembro. Por ejemplo, en

 static const char s_str[] = "bodacydo"; class Example { private: int *ptr; string name; string *pname; string &rname; const string &crname; int age; public: Example() : name(s_str, s_str + 8), rname(name), crname(name), age(-4) { } }; 

la lista de inicialización de mem del constructor no argumentos proporcionado por el usuario es name(s_str, s_str + 8), rname(name), crname(name), age(-4) . Esta lista de inicializadores de mem significa que el miembro de name es inicializado por el constructor std::string que toma dos iteradores de entrada , el miembro rname se inicializa con una referencia al name , el miembro crname se inicializa con una referencia const al name , y el miembro de age se inicializa con el valor -4 .

Cada constructor tiene su propia lista de inicializadores de mem , y los miembros solo se pueden inicializar en un orden prescrito (básicamente el orden en el que los miembros se declaran en la clase). Por lo tanto, los miembros de Example solo pueden inicializarse en el orden: ptr , name , pname , rname , crname y age .

Cuando no especifica un inicializador de memoría de un miembro, el estándar de C ++ dice:

Si la entidad es un miembro de datos no estático … del tipo de clase …, la entidad se inicializa por defecto (8.5). … De lo contrario, la entidad no se inicializa.

Aquí, dado que el name es un miembro de datos no estáticos del tipo de clase, se inicializa por defecto si no se especificó ningún inicializador para el name en la lista de inicializadores de mem . Todos los demás miembros de Example no tienen el tipo de clase, por lo que no se inicializan.

Cuando el estándar dice que no están inicializados, esto significa que pueden tener algún valor. Por lo tanto, como el código anterior no inicializó pname , podría ser cualquier cosa.

Tenga en cuenta que todavía tiene que seguir otras reglas, como la regla de que las referencias siempre deben inicializarse. Es un error del comstackdor no inicializar las referencias.

También puede inicializar los miembros de datos en el punto donde los declara:

 class another_example{ public: another_example(); ~another_example(); private: int m_iInteger=10; double m_dDouble=10.765; }; 

Utilizo este formulario de manera casi exclusiva, aunque he leído que algunas personas lo consideran “de mala calidad”, tal vez porque fue introducido recientemente, creo que en C ++ 11. Para mí es más lógico.

Otra faceta útil de las nuevas reglas es cómo inicializar los miembros de datos que son ellos mismos clases. Por ejemplo, supongamos que CDynamicString es una clase que encapsula el manejo de cadenas. Tiene un constructor que le permite especificar su valor inicial CDynamicString(wchat_t* pstrInitialString) . Es muy posible que utilice esta clase como miembro de datos dentro de otra clase, por ejemplo, una clase que encapsula un valor de registro de Windows que en este caso almacena una dirección postal. Para ‘codificar’ el nombre de la clave de registro al que escribe esto, usa llaves:

 class Registry_Entry{ public: Registry_Entry(); ~Registry_Entry(); Commit();//Writes data to registry. Retrieve();//Reads data from registry; private: CDynamicString m_cKeyName{L"Postal Address"}; CDynamicString m_cAddress; }; 

Tenga en cuenta que la segunda clase de cadena que contiene la dirección postal real no tiene un inicializador, por lo que se llamará a su constructor por defecto en la creación, quizás configurándolo automáticamente en una cadena en blanco.

Si su clase de ejemplo está instanciada en la stack, el contenido de los miembros escalares no inicializados es aleatorio y no está definido.

Para una instancia global, los miembros escalares no inicializados se pondrán a cero.

Para los miembros que son a su vez instancias de clases, se invocarán sus constructores predeterminados, por lo que su objeto de cadena se inicializará.

  • int *ptr; // puntero no inicializado (o puesto a cero si es global)
  • string name; // llamado constructor, inicializado con cadena vacía
  • string *pname; // puntero no inicializado (o puesto a cero si es global)
  • string &rname; // error de comstackción si no puede inicializar esto
  • const string &crname; // error de comstackción si no puede inicializar esto
  • int age; // valor escalar, no inicializado y aleatorio (o cero si es global)

Los miembros no estatizados no inicializados contendrán datos aleatorios. En realidad, solo tendrán el valor de la ubicación de memoria a la que están asignados.

Por supuesto, para los parámetros del objeto (como string ) el constructor del objeto podría hacer una inicialización predeterminada.

En tu ejemplo:

 int *ptr; // will point to a random memory location string name; // empty string (due to string's default costructor) string *pname; // will point to a random memory location string &rname; // it would't compile const string &crname; // it would't compile int age; // random value 

Los miembros con un constructor tendrán su constructor predeterminado llamado para la inicialización.

No puede depender del contenido de los otros tipos.

Si está en la stack, los contenidos de los miembros no inicializados que no tienen su propio constructor serán aleatorios e indefinidos. Incluso si es global, sería una mala idea confiar en que se eliminen a cero. Ya sea que esté en la stack o no, si un miembro tiene su propio constructor, se llamará para inicializarlo.

Por lo tanto, si tiene cadena * pname, el puntero contendrá basura aleatoria. pero para el nombre de la cadena, se llamará al constructor predeterminado para la cadena, que le da una cadena vacía. Para sus variables de tipo de referencia, no estoy seguro, pero probablemente sea una referencia a algún fragmento aleatorio de memoria.