C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++构造函数的初始化顺序

C++中构造函数的初始化顺序说明

作者:Littlewith

C++构造函数调用遵循基类→成员对象→派生类顺序,虚函数在构造中静态绑定,完成后动态绑定,拷贝构造在初始化、赋值及容器操作时调用,RVO优化可能避免拷贝,虚函数重写自子类起生效,不追溯基类

关键规则

对基类进行处理

对成员对象进行处理

派生类自己的构造函数

特殊情况

如果父类构造函数含有虚函数调用

#include <iostream>
using namespace std;
 
class A{
  public:
    A ():m_iVal(0){test();}//这里的test会调用父类的virtual void func()
    virtual void func() { std::cout<<m_iVal<<' ';}
    void test(){func();}
  public:
      int m_iVal;
};
class B : public A{
  public:
    B(){test();}//这里的test会调用父类的test()进而通过指针匹配
    virtual void func(){
      ++m_iVal;
      std::cout << m_iVal << ' ';
      }
};
int main(int argc ,char* argv[]){
  A*p = new B;
  p->test();
  return 0;
}

输出结果

0 1 2

虚函数调用规则

执行流程

vtable切换

输出

0 1 2。

核心结论

关于拷贝构造函数的初始化问题

#include<iostream>
using namespace std;

class MyClass {
public:
    MyClass(int i = 0) { // 构造函数
        cout << i;
    }
    MyClass(const MyClass &x) { // 拷贝构造函数
        cout << 2;
    }
    MyClass &operator=(const MyClass &x) { // 赋值运算符
        cout << 3;
        return *this;
    }
    ~MyClass() { // 析构函数
        cout << 4;
    }
};

int main() {
    MyClass obj1(1), obj2(2); // 创建 obj1 和 obj2
    MyClass obj3 = obj1;      // 创建 obj3 并初始化
    return 0;
}
MyClass obj3 = obj1;

由于obj3为被创建,那么调用拷贝构造函数,称为复制初始化

MyClass obj3; // 先默认构造
obj3 = obj1; // 再赋值

这时会导致调用赋值运算符

拷贝构造函数的什么时候被调用呢?

场景示例代码说明
对象初始化MyClass b = a;或者MyClass b(a);用已有对象初始化新对象
按值传递参数void func(MyClass x);函数参数创建副本
按值返回对象MyClass func() { … }返回局部对象(可能被优化,现如今的C++编译器普遍采用了RVO返回值优化导致返回时候不会进行拷贝)
容器操作vec.push_back(a);插入对象到容器
显式调用new MyClass(a);动态分配时拷贝

关于子类和父类的虚函数问题

如果父类函数不是 virtual,子类将其声明为 virtual:

#include <iostream>
class Base {
public:
    void foo() { // 非虚函数
        std::cout << "Base::foo()" << std::endl;
    }
};

class Derived : public Base {
public:
    virtual void foo() { // 子类声明为虚函数
        std::cout << "Derived::foo()" << std::endl;
    }
};

class GrandDerived : public Derived {
public:
    void foo() { // 重写 Derived 中的虚函数
        std::cout << "GrandDerived::foo()" << std::endl;
    }
};

int main() {
    Base* b1 = new Derived();
    b1->foo(); // 输出 "Base::foo()"

    Derived* d1 = new Derived();
    d1->foo(); // 输出 "Derived::foo()"

    Base* b2 = new GrandDerived();
    b2->foo(); // 输出 "Base::foo()"

    Derived* d2 = new GrandDerived();
    d2->foo(); // 输出 "GrandDerived::foo()"
    delete b1; delete d1; delete b2; delete d2;
    return 0;
}

父类函数非虚,没有多态

子类声明为虚,影响后续继承

隐藏而非重写

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文