关于类继承的构造与析构调用分析

  总体结论:派生类的构造函数在创建对象时调用,调用顺序如下:

       1.调用虚基类的构造函数(若有多个虚基类,调用顺序为继承的顺序。);

       2.调用基类的构造函数(若有多个基类,调用顺序为继承的顺序。);

       3.调用类类型的成员对象的构造函数(调用顺序为定义的顺序);

       4.调用自身的构造函数。

       析构函数的调用顺序一般和构造函数的调用顺序相反,但分如下几种情况:

       1.当父类的指针 new一个子类的临时对象时。若父类的析构不是虚函数,则delete的时候不调用子类的析构,只调用父类的析构;

          若父类的析构是虚函数,则delete的时候先调用子类的析构,后调用父类的析构;

       2.当子类的指针 new一个子类的临时对象时。不管父类的析构是否为虚函数,delete的时候都会先调用子类的析构,后调用父类的析构;

  代码验证:

      

class A {
public:
    A() {
        cout << " A" << endl;
    }
    ~A() {  //不是虚函数
        cout << "~ A" << endl;
    }
};

class B {
public:
    B() {
        cout << " B" << endl;
    }
    ~B() {  //不是虚函数
        cout << "~ B" << endl;
    }
};

class C:public A,public B
{
public:
    C() {
        cout << " C" << endl;
    }
    ~C() {
        cout << "~ C" << endl;
    }
};

int main() {
    A *a = new C();
    delete a;
    return 0;
}
/*运行结果 :
A
B
C
~A
*/
class A {
public:
    A() {
        cout << " A" << endl;
    }
    virtual ~A() {  //是虚函数
        cout << "~ A" << endl;
    }
};

class B {
public:
    B() {
        cout << " B" << endl;
    }
    virtual ~B() {  //是虚函数
        cout << "~ B" << endl;
    }
};

class C:public A,public B
{
public:
    C() {
        cout << " C" << endl;
    }
    ~C() {
        cout << "~ C" << endl;
    }
};

int main() {
    A *a = new C();
    delete a;
    return 0;
}
/*运行结果 :
A
B
C
~ C
~ B
~ A
*/
class A {
public:
    A() {
        cout << " A" << endl;
    }
    virtual ~A() {  //是虚函数
        cout << "~ A" << endl;
    }
};

class B {
public:
    B() {
        cout << " B" << endl;
    }
    virtual ~B() {  //是虚函数
        cout << "~ B" << endl;
    }
};

class C:public A,public B
{
public:
    C() {
        cout << " C" << endl;
    }
    ~C() {
        cout << "~ C" << endl;
    }
};

int main() {
    C *c = new C();
    delete c;
    return 0;
}
/*运行结果 :
A
B
C
~ C
~ B
~ A
*/
class A {
public:
    A() {
        cout << " A" << endl;
    }
       ~A() {  //不是虚函数
        cout << "~ A" << endl;
    }
};

class B {
public:
    B() {
        cout << " B" << endl;
    }
         ~B() {  //不是虚函数
        cout << "~ B" << endl;
    }
};

class C:public A,public B
{
public:
    C() {
        cout << " C" << endl;
    }
    ~C() {
        cout << "~ C" << endl;
    }
};

int main() {
    C *c = new C();
    delete c;
    return 0;
}
/*运行结果 :
A
B
C
~ C
~ B
~ A
*/    

  原因分析:

      1.当父类析构函数不是虚函数时,进行编译时调用,只调用自身的析构函数。

      2.当父类析构函数是虚函数时,进行运行时调用,无论指针指的那个对象,系统都会调用相关类的析构函数。

       程序中最好把父类的析构函数声明虚函数,这样所有子类的析构函数也为虚函数,也为了防止内存泄露。

原文地址:https://www.cnblogs.com/parzulpan/p/9744216.html

时间: 2024-11-10 11:50:41

关于类继承的构造与析构调用分析的相关文章

继承的构造和析构

思考问题:如何初始化父类成员?父类构造函数和子类构造函数有什么关系呢? 子类对象的构造 子类中可方式以定义构造函数 子类构造函数必须对继承而来的成员进行初始化: 1)直接通过初始化列表或者赋值的方式进行初始化 2)调用父类构造函数进行初始化 父类构造函数在子类中的调用方式 默认调用:适用于无参构造函数和使用默认参数的构造函数 显示调用:通过初始化列表进行调用,适用于所有父类构造函数 如果子类没有显示调用,那么在父类中需要有无参构造函数 class Child : public Parent {

C++ 派生类对象的构造与析构过程

C++ 派生类对象的构造与析构过程 因为基类的成员变量和派生类的成员变量在内存中的连续的(下面程序会验证这一点),如下图所示: 所以构造派生类对象的时候,构造成员变量的过程就像入栈一样: 那么很自然,派生类对象析构的时候就是出栈的过程,先进后出,后进先出: 下边一个简单的程序验证这个结论: #include <iostream> using namespace std; class base{ int a; public: base(const int& a):a(a){cout<

C++ 类成员的构造和析构顺序

我想对面向对象有了解的童鞋应该不会对类和对象感到陌生吧 ! 对象并不是突然建立起来的,创建对象必须时必须同时创建父类以及包含于其中的对象.C++遵循如下的创建顺序: (1)如果某个类具体基类,执行基类的默认构造函数. (2)类的非静态数据成员,按照声明的顺序创建. (3)执行该类的构造函数. 即构造类时,会先构造其父类,然后创建类成员,最后调用本身的构造函数. 下面看一个例子吧 class c { public: c(){ printf("c\n"); } protected: pri

java 类继承,父类子类方法调用的过程d

1.Mytank类继承tank,则Mytank里面的公有部分即public 成员和方法在Mytank中是含有的可以调用和赋值,且在MyTank中不要有新的成员变量与tank中的公有成员名称一样,这样会发生调用紊乱,如: Tank tank = new MyTank(); int c = tank.a; 若 tank类中public a= 3: Mytank子类中新定义public a = 2: 则上述代码中的调用将会使得c = 3: 用了父类中的a.

java 类继承,父类子类方法调用的过程

1.Mytank类继承tank,则Mytank里面的公有部分即public 成员和方法在Mytank中是含有的可以调用和赋值,且在MyTank中不要有新的成员变量与tank中的公有成员名称一样,这样会发生调用紊乱,如: Tank tank = new MyTank(); int c = tank.a; 若 tank类中public a= 3: Mytank子类中新定义public a = 2: 则上述代码中的调用将会使得c = 3: 用了父类中的a.

[类和对象]构造和析构

有关构造函数  1构造函数定义及调用 1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数: 2)构造函数在定义时可以有参数: 3)没有任何返回类型的声明. 2构造函数的调用 自动调用:一般情况下C++编译器会自动调用构造函数 手动调用:在一些情况下则需要手工调用构造函数 有关析构函数  析构函数定义及调用 1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数 语法:~ClassName() 2)析构函数没有参数也没有任何返回类型的

继承的构造和析构顺序

程序示例 1 #include <iostream> 2 using namespace std; 3 class a 4 { 5 public: 6 a(){cout<<"构造a"<<endl;} 7 ~a(){cout<<"析构a"<<endl;} 8 }; 9 class b 10 { 11 public: 12 b(){cout<<"构造b"<<endl;

完整类实现:构造,析构,遍历二叉树

根据前面一个博文内容已经讲述了如何根据两种遍历方式进行构建二叉树 这里利用递归方式遍历二叉树,递归方式比较简单,后续补充其余非递归方式 再此主要是完善类的使用: 其中重点在于:接口定义 二叉树的析构删除 以及类成员变量中如果有指针,同时涉及复制构造函数和赋值操作符函数时需要用到的智能指针 如果接口方面定义不够好,还望包涵 如果有对智能指针不理解的地方,可以移步 http://blog.csdn.net/xietingcandice/article/details/39670269 .h文件 #i

C++--第16课 - 继承中的构造与析构

第16课 - 继承中的构造与析构 1. 赋值兼容性原则 (1)      子类对象可以当作父类对象使用. (2)      子类对象可以直接赋值给父类对象. (3)      子类对象可以直接初始化父类对象. (4)      父类指针可以直接指向子类对象. (5)      父类引用可以直接引用子类对象. 子类就是特殊的父类 #include <cstdlib> #include <iostream> using namespace std; class Parent { pro