2010-06-27 14 views
95

Supongamos que tengo una clase con memebers privados ptr, name, pname, rname, crname y age. ¿Qué pasa si no los inicializo yo mismo? He aquí un ejemplo:¿Cómo se inicializan los miembros de la clase C++ si no lo hago explícitamente?

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

    public: 
     Example() {} 
}; 

Y entonces hago:

int main() { 
    Example ex; 
} 

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

¿Qué más debería saber?

¿Alguien conoce 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 programas (sin errores). ¡Cualquier comentario ayudaría!

+1

Para recomendaciones de libros, consulte http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list –

+0

Mike, uy, me refiero al capítulo de algún libro que lo explica. ¡No es un libro completo! :) – bodacydo

+0

Probablemente sería una buena idea leer todo un libro en un idioma en el que intenta programar. Y si ya leíste uno y no explicaba esto, entonces no era un libro muy bueno. –

Respuesta

135

En lugar de la 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 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 compilación si no lo inicializa explícitamente.

Para tipos primitivos (punteros, enteros, etc.), que son no inicializado - contienen todo lo no deseado arbitraria pasó a estar en esa ubicación de memoria con anterioridad.

Para referencias (por ejemplo std::string&), es ilegal no inicializar ellos, y su compilador se quejará y se niegan a compilar dicho código. Las referencias siempre deben ser inicializadas.

Así, 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 
+3

+1. Vale la pena señalar que, según la estricta definición estándar, las instancias de tipos primitivos junto con una variedad de otras cosas (cualquier región de almacenamiento) se consideran * objetos *. – stinky472

+0

Cierto, pero si el OP hubiera leído el estándar, no haría la pregunta, por lo que no creo que sea confuso usar * object * en el sentido general de programación orientada a objetos de algo de clase, en oposición al tipo primitivo. :) –

+5

"Si la clase del objeto no tiene un constructor predeterminado, será un error de compilación si no lo inicializa explícitamente" ¡eso está mal **! Si una clase no tiene un constructor predeterminado, se le otorga un * constructor por defecto * predeterminado que está vacío. – Wizard

7

Si su clase de ejemplo se crea una instancia en la pila, 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 mundial)
  • string name; // constructor de llamada, inicializado con la cadena vacía
  • string *pname; // puntero no inicializado (llevada a cero si mundial)
  • string &rname; // error de compilación si no puede inicializar este
  • const string &crname; // error de compilación si no puede inicializar este
  • int age; // valor escalar, no inicializado y aleatorio (o cero si es global)
+0

Experimenté y parece que 'string name' está vacío después de inicializar la clase en la pila. ¿Estás absolutamente seguro de tu respuesta? – bodacydo

+1

cadena tendrá un constructor que proporcionó una cadena vacía de forma predeterminada - Voy a aclarar mi respuesta –

+0

@bodacydo: Paul está en lo cierto, pero si te importa este comportamiento, nunca está de más ser explícito. Tíralo en la lista de inicializadores. – Stephen

4

Los miembros no estáticos 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 su 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 
0

Miembros con un constructor tendrán su constructor por defecto llamado para la inicialización.

No puede depender del contenido de los otros tipos.

0

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

Por lo tanto, si tiene cadena * pname, el puntero contendrá basura al azar. 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.

21

En primer lugar, déjame explicarte qué mem-initializer-list es. A mem-inicializador-lista es una lista separada por comas de mem-inicializador s, donde cada mem-inicializador es un nombre de miembro seguido por (, seguido de un expresión-lista, seguido de un ) . 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) 
    { 
    } 
}; 

el inicializador-lista mem del constructor suministrados por el usuario, no hay argumentos es name(s_str, s_str + 8), rname(name), crname(name), age(-4). Este mem-inicializador-lista significa que el miembro de name se inicializa por the std::string constructor that takes two input iterators, el miembro de rname se inicializa con una referencia a name, el miembro crname se inicializa con un const-referencia a name, y el miembro de age se inicializa con el valor -4.

Cada constructor tiene su propia mem-initializer-list, 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 se pueden inicializar en el orden: ptr, name, pname, rname, crname y age.

Cuando no se especifica un mem-inicializador de un miembro, el estándar C++ dice:

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

Aquí, debido name es un miembro de datos no estático del tipo de clase, es default-inicializado si no hay inicializador para name se especificó en el mem-inicializador lista. 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 cualquier 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 compilador no inicializar las referencias.

8

También puede inicializar miembros de datos en el punto que se declaran:

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

utilizo esta forma casi exclusiva, aunque he leído algunas personas consideran que es 'mala forma', quizás porque sólo era presentado 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 a su vez 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 clave de registro en el que esto escribe se utiliza 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; 
}; 

nota la segunda clase cadena que contiene la dirección postal real no tiene un inicializador por lo que su constructor por defecto será llamado en la creación - quizás configurándolo automáticamente en una cadena en blanco.

Cuestiones relacionadas