c++ - tipos - palabra reservada que se usa para acceder a una variable de instancia oculta en un método
¿Cómo se inicializan los miembros de la clase C++ si no lo hago explícitamente? (7)
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 compilació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 compilador se quejará y se negará a compilar 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
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 programas (sin errores). ¡Cualquier comentario ayudaría!
Los miembros con un constructor tendrán su constructor predeterminado llamado para la inicialización.
No puede depender del contenido de los otros tipos.
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
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 compilador no inicializar las referencias.
Si está en la pila, 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 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 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.
Si su clase de ejemplo está instanciada 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 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 compilación si no puede inicializar esto -
const string &crname;
// error de compilación si no puede inicializar esto -
int age;
// valor escalar, no inicializado y aleatorio (o cero si es global)
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.