C++ 类对象的初始化顺序

本文详细介绍了C++中构造函数的调用顺序规则,包括基类与派生类构造函数、成员类构造函数之间的调用顺序,并通过具体代码示例进行了说明。

C++构造函数调用顺序

1.     创建派生类的对象,基类的构造函数优先被调用(也优先于派生类里的成员类);

2.    如果类里面有成员类,成员类的构造函数优先被调用;(也优先于该类本身的构造函数)

3.     基类构造函数如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序而不是它们在成员初始化表中的顺序;

4.     成员类对象构造函数如果有多个成员类对象,则构造函数的调用顺序是对象在类中被声明的顺序而不是它们出现在成员初始化表中的顺序;

5.     派生类构造函数,作为一般规则派生类构造函数应该不能直接向一个基类数据成员赋值而是把值传递给适当的基类构造函数,否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。(基类设计者的责任是提供一组适当的基类构造函数)

综上可以得出,初始化顺序:

父类构造函数–>成员类对象构造函数–>自身构造函数

其中成员变量的初始化与声明顺序有关,构造函数的调用顺序是类派生列表中的顺序。
析构顺序和构造顺序相反

下面是最近笔试的某网络公司的题目:

#include <iostream>
using namespace std;

class A
{
      public:
            A(){ cout<<“A”<<endl;}
            virtual ~A(){ cout<<“~A”<<endl; }
};

class B: public A
{
      public:
             B(){ cout<<“B”<<endl;}
             ~B() {cout<<“~B”<<endl; }
      private:
              A a;
};

class C: public A, public B     //类在派生表中的继承列表
{
      public:
              C() {cout<<“C”<<endl;}
              ~C() {cout<<“~C”<<endl; }
      private:
              B b;
      public:
             A a;
};

int main()
{
    C * p = new C;
    delete p;
   
    system(“PAUSE”);
    return 0;
}

结果和分析:

A          //1 父类A的构造函数
A         //2 父类B中A的构造函数
A         //3 父类B中成员变量b初始化 (调用父类A的构造函数)
B         //4  父类B中成员变量b初始化 (调用父类B的构造函数)
A        //5   C中成员变量b 的构造
A
B
A         //6 C中成员变量a的构造
C         //7 C的构造函数最后调用 (finally ,-__-|||)
~C
~A
~B
~A
~A
~B
~A
~A
~A

====================================

综上可以看出,1~4 按照在派生表中的出现次序进行初始化,首先调用父类的构造函数

                          5, 6 调用 成员变量的构造函数

                         7 调用自身的构造函数

PS:更复杂的情况,可以试下虚继承。

在有虚继承和一般继承存在的情况下,优先虚继承

例如class C: public B, virtual public A

则先调用A的构造函数,再调用B的构造函数

=====================================

转自:https://www.cnblogs.com/forcheryl/p/4205497.html


### C++类对象初始化顺序规则解释 在 C++ 中,类对象初始化遵循严格的规则以确保成员变量按照预期的方式被正确初始化。以下是关于类对象初始化顺序的关键点: #### 1. 构造函数初始值列表的作用 构造函数初始值列表仅指定用于初始化成员变量的值[^3]。然而,实际的初始化顺序并不依赖于初始值列表中书写的顺序,而是严格依据这些成员变量在定义中的声明顺序。 #### 2. 成员变量的初始化顺序 无论构造函数初始值列表如何书写,成员变量总是按其在定义中出现的顺序进行初始化。如果初始值列表中的顺序定义中的顺序不同,则可能导致逻辑错误或未定义行为。例如,在下面的例子中: ```cpp class Example { private: int a; int b; public: Example(int x, int y) : b(x), a(y) {} // 初始值列表先写b再写a }; ``` 尽管 `b` 出现在初始值列表之前,但由于 `a` 在定义中位于 `b` 的前面,因此 `a` 将会优先被初始化。 #### 3. 对象内部组件的初始化顺序 对于复杂的结构(如继承关系),初始化顺序如下: - 首先是基构造函数被执行。 - 接着是当前中成员变量按照声明顺序依次初始化。 - 最后才是派生自身的构造函数体执行[^5]。 #### 4. 特殊型的成员变量处理 某些特殊型的成员变量需要特别注意: - **常量 (`const`) 成员变量**:必须通过成员初始化列表显式初始化,因为在构造函数体内无法对其进行赋值操作[^4]。 - **引用型成员变量**:同样需要在成员初始化列表中完成绑定,因为引用一旦声明就必须立即关联到某个有效的实体上。 #### 示例代码展示 以下是一段综合演示上述规则的代码片段: ```cpp #include <iostream> using namespace std; class Base { public: Base() { cout << "Base Constructor\n"; } }; class Derived : public Base { private: const int c_value; int& ref_var; int normal_var; public: Derived(int val) : ref_var(normal_var), c_value(val), normal_var(42) { cout << "Derived Constructor: " << "c_value=" << c_value << ", ref_var=" << ref_var << ", normal_var=" << normal_var << "\n"; } }; int main() { Derived d(10); return 0; } ``` 运行此程序时,输出结果表明即使我们在初始值列表里调整了顺序,最终还是依照定义内的次序来完成各部分的初始化过程。 ### 总结 综上所述,C++ 类对象初始化顺序主要取决于以下几个方面:首先是基构造;其次是本成员基于声明位置逐一启动;最后才轮到具体业务逻辑填充数据等内容展开运作[^5]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值