初始化列表和声明的顺序之间的关系
类中数据成员的声明顺序和初始化顺序之间的关系?
在类中的声明顺序是不是一定和初始化顺序相一致,不一致会出现什么结果,为什么要一致?
类的数据成员的初始化顺序是按照类内数据成员的声明顺序进行初始化的,这样就可以减少不必要的开销,这样的话类就不必要为每一个对象进行跟踪初始化数据成员,只需要按照类中的声明顺序进行初始化就可以了。
如果允许上面的情况(即,成员按它们在初始化列表上出现的顺序被初始化)发生,编译器就要为每一个对象跟踪其成员初始化的顺序,以保证它们的析构函数以正确的顺序被调用。这会带来昂贵的开销。所以,为了避免这一开销,同一种类型的所有对象在创建(构造)和摧毁(析构)过程中对成员的处理顺序都是相同的,而不管成员在初始化列表中的顺序如何。
另外,基类数据成员总是在派生类数据成员之前被初始化,所以使用继承时,要把基类的初始化列在成员初始化列表的最前面。
看下面的例子:
#include <iostream>
#include<vector>
using namespace std;
template<class t>
class Array {
public:
Array(int lowbound, int highbound);
void Print()const;
private:
vector<t> data; // 数组数据存储在vector对象中
size_t size; // 数组中元素的数量
int lbound, hbound; // 下限,上限
//vector<t> data; // 数组数据存储在vector对象中
};
template<class t>
void Array<t>::Print() const
{
cout << "Print the size_t : " << size << endl;
cout << "Print the data : " << endl;
cout << data[1]<< endl;
}
template<class t>
Array<t>::Array(int lowbound, int highbound)
: size(highbound - lowbound + 1),
lbound(lowbound), hbound(highbound),data(size)
{}
int main()
{
Array<int> Temp(10,20);
Temp.Print();
return 0;
}
这段代码中的初始化列表为:: size(highbound - lowbound + 1),lbound(lowbound), hbound(highbound),data(size)
声明的顺序为: vector<t> data; // 数组数据存储在vector对象中
size_t size; // 数组中元素的数量
int lbound, hbound; // 下限,上限
编辑器会按照声明顺序进行初始化,那么首先初始化vector<t> data; 由于vector<t> data; 的初始化是data(size),而size有没有被初始化,所以这段代码是有隐患的。
所以声明时应该这样声明:
size_t size; // 数组中元素的数量
int lbound, hbound; // 下限,上限
vector<t> data; // 数组数据存储在vector对象中
记住:初始化列表中成员列出的顺序和成员在类内声明的顺序一致。
分享到:
相关推荐
初始化顺序方面,同一编译单元内的全局变量按照声明顺序进行初始化,但不同编译单元间的全局变量的初始化顺序是不确定的,这可能导致潜在的问题。如果全局变量之间存在依赖关系,最佳实践是重构代码以消除这种依赖。...
这显示了成员初始化的顺序与声明顺序之间的关系,而非初始化列表中的顺序。 初始化列表的重要性在于处理特定类型的成员变量,如`const`修饰的成员和引用类型成员。对于`const`成员变量,一旦在构造函数初始化列表中...
例如,如果父类和子类都包含了某些静态变量,并且这些变量之间存在依赖关系,不正确的初始化顺序可能导致程序错误。同样,如果实例变量或初始化块依赖于另一个类(尤其是父类)中初始化的结果,不正确的顺序也可能...
Makefile中的编译规则可以设置模块之间的依赖关系,这样在编译时就可以通过指定的编译顺序来控制模块的加载顺序。 最后,编译器本身也提供了一定程度的控制。通过编译器选项和特定的编译指令,可以影响内核模块的...
这种机制确保了模块能够按照预定的顺序加载和初始化。 #### 内核模块调用框架控制 内核模块加载顺序的一个关键方面是内核自身的调用框架。当内核启动时,它会遍历这些初始化函数段,并按顺序调用这些函数。这是...
多继承的构造函数调用顺序按它们在被继承时所声明的顺序(从左到右)依次调用,与它们在初始化列表中的顺序无关。 构造函数回顾 在派生类的对象中,由基类中声明的数据成员和函数成员所构成的封装体称为基类子对象...
在顺序存储结构中,线性表的元素被存储在一个一维数组中,按照它们在表中的相对位置来表示数据之间的逻辑关系。这种方式简单直观,易于理解和操作。 线性表的顺序存储具有以下特点: 1. **连续存储**:所有元素存储...
对于静态变量和静态初始化块、实例变量和实例初始化块之间的初始化顺序,实际上取决于它们在类中的声明顺序。 例如,如果静态变量出现在静态初始化块之前,则静态变量先初始化;反之亦然。同理,对于实例部分也是...
1. 实现数组的动态分配和释放,如`void Create(int size)`初始化数组,`void Destroy()`释放数组。 2. 插入操作`bool Insert(int index, const Type& elem)`需要检查插入位置是否合法,然后移动元素并将新元素插入。...
这意味着先声明的成员先初始化,这对于管理依赖关系非常重要。 5. **拷贝构造函数** 拷贝构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。默认的拷贝构造函数执行逐个成员的浅复制,但有时...
构造函数在对象创建时自动调用,用于初始化对象成员。析构函数在对象销毁前调用,用于清理资源。在继承链中,基类的构造函数会先于派生类的构造函数执行,而析构函数的执行顺序则相反。 **8. 静态成员与静态函数** ...
1. 初始化:创建一个空的顺序表,或者根据给定大小初始化一个顺序表。 2. 插入元素:在指定位置插入一个新元素,可能需要移动数组中的元素以腾出空间。 3. 删除元素:根据索引删除一个元素,同样需要移动数组元素以...
初始化的顺序与它们在类定义中的声明顺序相同,而不是按照初始化列表中的顺序。 ```cpp Derived::Derived(...) : member1(value1), member2(value2) { ... } ``` 3. **派生类构造函数体的执行**:最后,派生类...
- 在组合类的构造函数中,初始化列表中的顺序决定了成员对象初始化的先后顺序,而这个顺序与成员在类中声明的顺序一致。 #### 三、组合的示例 假设有一个`Person`类,其中包含了`Address`类的对象作为成员变量。 ...
- **成员初始化顺序**:成员变量的初始化顺序遵循它们在类中的声明顺序,而非初始化列表中的顺序。因此,应确保成员变量按照类中的顺序初始化,并避免在一个成员变量的初始化过程中使用尚未初始化的其他成员变量。 -...
然而,变量的初始化是在执行阶段完成的,因此如果没有赋值,变量的值将是`undefined`。同样,函数声明在预编译阶段也被处理,允许在声明前调用函数,而函数表达式(如`var f = function(){};`)则不同,它需要在执行...
总结来说,本节课的重点是理解C++中的继承、多继承、虚函数和虚基类的概念及其用法,以及在处理继承关系时如何正确初始化和调用成员,特别是在涉及多继承和二义性问题时。掌握这些概念对于编写复杂和可扩展的C++代码...
成员初始化顺序与声明顺序一致,确保依赖关系正确。引用类型的成员必须在初始化时立即赋值。 4. **赋值运算符**(`operator=`)默认实现可能导致浅拷贝问题,应重载以实现深拷贝。赋值运算符通常返回被赋值对象的...