int main()
{
int i = 0; // <--- El dato 'i' nace en este punto.
return i; // <--- El dato 'i' muere en este punto.
}
int在上面的代码中,被调用的类型变量i在定义()时开始其生命周期int i = 0;,当它离开定义它的范围(函数main)时,它结束其生命周期。让我们看另一个例子:
int main()
{
int i = 0; // <--- El dato 'i' nace en este punto.
{
char c = 1; // <--- El dato 'c' nace en este punto.
} // <--- El dato 'c' muere en este punto.
float f = 2.f; // <--- El dato 'f' nace en este punto.
return i; // <--- Los datos 'i' y 'f' mueren en este punto.
}
int main()
{
int a = 0; // <--- El dato 'a' nace en este punto.
// v--- El dato 'i' nace en este punto.
for (int i = 0; i < 100; ++i)
{
a += i;
} // <--- El dato 'i' muere en este punto.
return 0; // <--- El dato 'a' muere en este punto.
}
当一条数据是另一个对象的一部分时,它的生命周期与它所属的对象的生命周期相同:
struct S { int i; char c; float f; };
int main()
{
int i = 0; // <--- El dato 'i' nace en este punto.
S s; /* <--- El dato 's' nace en este punto, esto implica que...
... s.i, s.c, s.f nacen en el mismo punto. */
return 0; /* <--- Los datos 'i' y 's' mueren en este punto...
... esto implica que s.i, s.c, s.f tambien mueren aqui */
}
struct S {
int i; char c; float f;
S() { // Constructor de S
i = 1; // El dato 'i' ya existia antes de entrar en S::S()
c = 'c'; // El dato 'c' ya existia antes de entrar en S::S()
f = 0xf; // El dato 'f' ya existia antes de entrar en S::S()
}
};
struct S {
const int entero_constante;
int &referencia_a_entero;
S()
{
entero_constante = 1; // Error!
referencia_a_entero = entero_constante; // Error!
}
};
由于它S::entero_constante是被限定为常量的数据,因此当它的生命周期已经开始时,不可能修改它的值。由于在 C++ 中不能更改引用所引用的对象,因此需要在引用开始生命周期时为其赋值,因此我们使用的方式S::referencia_a_entero不正确1;为了解决这个问题,我们必须使用构造函数的初始化列表:
int otro_entero = 1;
struct S {
const int entero_constante;
int &referencia_a_entero;
S() :
entero_constante{1},
referencia_a_entero{otro_entero}
{
}
};
构造函数初始化器列表也用于在缺少默认构造函数的子对象上调用构造函数:
struct Punto {
int x, y;
// Punto carece de constructor por defecto!
Punto(int a, int b) :
x{a},
y{b}
{}
};
struct Jugador {
std::string nombre;
Punto posicion;
};
int main()
{
Jugador jugador1, jugador2; /* Error! no podemos crear instancias de
Jugador porque Jugador::posición carece
de constructor por defecto y por ello no
se sabe como construir el objeto */
std::cin >> jugador1.nombre >> jugador2.nombre;
return 0;
}
易于修复:
struct Punto {
int x, y;
Punto(int a, int b) :
x{a},
y{b}
{}
};
struct Jugador {
std::string nombre;
Punto posicion;
Jugador() :
posicion{0, 0} // Llamada al constructor de Punto
{}
};
int main()
{
Jugador jugador1, jugador2; /* Correcto! ambas instancias de Jugador
tienen el sub-objeto posicion inicializado */
std::cin >> jugador1.nombre >> jugador2.nombre;
return 0;
}
构造函数初始化列表也用于调用基类的构造函数:
struct Punto {
int x, y;
// Punto carece de constructor por defecto!
Punto(int a, int b) :
x{a},
y{b}
{}
};
struct Punto3D : public Punto {
int z;
Punto3D() :
Punto{0, 0}, // Llamada al constructor de Punto
z{0}
{}
};
并且(最后)它还用于将构造委托给另一个构造函数:
struct Punto {
int x, y;
// Constructor con parametros
Punto(int a, int b) :
x{a},
y{b}
{}
// Constructor por defecto, delegando en el constructor con parametros
Punto() : Punto{0, 0}
{}
};
要回答这个问题,首先要明确一条数据的“生命周期”的概念。
生命周期。
一个数据从它被实例化的那一刻起就一直存在,直到它离开它被实例化的范围,例如:
int
在上面的代码中,被调用的类型变量i
在定义()时开始其生命周期int i = 0;
,当它离开定义它的范围(函数main
)时,它结束其生命周期。让我们看另一个例子:我们看到数据在定义时诞生,在离开定义的作用域时死亡。在前面的示例中,函数
main
有一个子作用域,其中定义了一个类型char
为调用的变量c
,结束了它的生命周期在数据诞生之前.f
的类型float
,因为它的范围在定义之前结束f
。这个子范围的事情是在循环中发生的
for
:当一条数据是另一个对象的一部分时,它的生命周期与它所属的对象的生命周期相同:
我们将更详细地研究属于其他对象的数据,因为它们的生命周期从它们所属的对象被创建时开始,当我们进入构造函数的主体时,这些对象已经存在......否则!我们不能使用它们!:
如果,在进入构造函数体的时候,数据已经存在,那么生命周期是从什么时候开始的呢?我们可以想象它们诞生在构造函数的定义和构造函数体的作用域开始之间:
明确了对象的子对象的生命周期后,我们就可以解决这个问题了……
构造函数初始化列表的用途是什么?
有时您需要在某个类型的子对象开始其生命周期时为其赋予价值,这称为初始化。某些类型的数据必然需要初始化,例如常量或引用,因此以下代码无法编译:
由于它
S::entero_constante
是被限定为常量的数据,因此当它的生命周期已经开始时,不可能修改它的值。由于在 C++ 中不能更改引用所引用的对象,因此需要在引用开始生命周期时为其赋值,因此我们使用的方式S::referencia_a_entero
不正确1;为了解决这个问题,我们必须使用构造函数的初始化列表:构造函数初始化器列表也用于在缺少默认构造函数的子对象上调用构造函数:
易于修复:
构造函数初始化列表也用于调用基类的构造函数:
并且(最后)它还用于将构造委托给另一个构造函数:
概括。
构造函数初始化列表用于:
1因为我们试图以非常量的方式引用一个恒定的数据。