码迷,mamicode.com
首页 > 其他好文 > 详细

继承中的构造与析构(三十九)

时间:2018-05-27 12:11:59      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:C++   继承   构造顺序   析构顺序   初始化列表   

        我们思考下这个问题:如何初始化父类成员?父类构造函数和子类构造函数有何关系呢?在子类中可以定义构造函数,子类构造函数必须对继承而来的成员进行初始化:a> 直接通过初始化列表或者赋值的方式进行初始化;b> 调用父类构造函数进行初始化。

        下来我们来说说父类构造函数在子类中的调用方式,分为两种:a> 默认调用:适用于无参构造函数和使用默认参数的构造函数;b> 显示调用:通过初始化列表进行调用,适用于所有父类构造函数。那么隐式调用是在子类的构造函数中啥都不加,显示调用时在子类构造函数后加上父类构造函数,如下所示

技术分享图片

        下来我们就对子类的构造函数一探究竟

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(string s)
    {
        cout << "Parent(string s): " << s << endl;
    }
};

class Child : public Parent
{
public:
    Child()
    {
        cout << "Child()" << endl;
    }
    
    Child(string s) : Parent(s)
    {
        cout << "Child(string s): " << s << endl;
    }
};

int main()
{
    Child c;
    
    Child cc("cc");
    
    return 0;
}

        我们先来分析下,在子类 Child 中,它定义的第一个构造函数显然是隐式调用父类的构造函数。但是在父类的构造函数中,我们既没有定义无参构造函数,也没有定义默认参数的构造函数,所以这个隐式调用肯定会出错。而第二个对象 cc 是进行显示调用的,所以它不会报错。我们来看看编译结果

技术分享图片

        它报错说第 19 行出错,也就是子类的隐调用出错了,下来我们在父类中通过一个无参构造函数,来看看编译是否还会出错呢

技术分享图片

        我们看到编译通过了,并且也完美运行。我们来说说子类对象的构造规则:a> 子类对象在创建时会首先调用父类的构造函数;b> 先执行父类的构造函数再执行子类的构造函数;c> 父类构造函数可以被隐式调用或者显示调用。那么子类对象的创建时构造函数的调用又有什么顺序呢?1、调用父类的构造函数;2、调用成员变量的构造函数;3、调用类自身的构造函数。对此,有唐长老总结的一个口诀心法:先父母,后客人,再自己

        下来我们通过编程来看看子类创建时构造函数的执行顺序

#include <iostream>
#include <string>

using namespace std;

class Object
{
    string ms;
public:
    Object(string s)
    {
        ms  = s;
        
        cout << "Object(string s): " << ms << endl;
    }
};

class Parent : public Object
{
    string ms;
public:
    Parent() : Object("Default")
    {
        ms = "Default";
    
        cout << "Parent()" << endl;
    }

    Parent(string s) : Object(s)
    {
        ms = s;
    
        cout << "Parent(string s): " << s << endl;
    }
};

class Child : public Parent
{
    Object mOb1;
    Object mOb2;
    string ms;
public:
    Child() : mOb1("Default 1"), mOb2("Default 2")
    {
        ms = "Default";
    
        cout << "Child()" << endl;
    }
    
    Child(string s) : Parent(s), mOb1(s + " 1"), mOb2(s + " 2")
    {
        ms = s;
        
        cout << "Child(string s): " << s << endl;
    }
};

int main()
{
    Child c;
    
    // c output:
    //   Object(string s): Default
    //   Parent()
    //   Object(string s): Default 1
    //   Object(string s): Default 2
    //   Child()
    
    cout << endl;
    
    Child cc("cc");
    
    // cc output:
    //   Object(string s): Default
    //   Parent(string s) : cc
    //   Object(string s): cc 1
    //   Object(string s): cc 2
    //   Child(string s): cc
    
    return 0;
}

        我们来分析下,类Child c 创建时首先会隐式调用它的父类构造函数 Parent() : Object("Default"),而 Parent 创建时会先调用它的父类 Object 的构造函数 Object("Default")。再来调用成员对象 mOb1 和 mOb2 的构造函数 mOb1("Default 1"), mOb2("Default 2"),最后调用自己的构造函数 Child()。所以最后打印的应该和我们程序中写的是一致的。再来看看对象 cc 的创建过程,因为它是显示调用,所以会调用构造函数 Parent(s) ,而 Parent 的父类 Object 也会调用构造函数 Object(string s) 。额庵后调用成员对象 mOb1 和 mOb2 的构造函数 mOb1(s + "1"), mOb2(s + "2"),最后调用自己的构造函数 Child(string s)。打印的应该也和我们在程序中写的一致。我们来看编译结果

技术分享图片

        结果和我们分析的是一致的。那么再来看看析构函数的调用顺序,它跟构造函数的顺序刚好相反:1、执行自身的析构函数;2、执行成员变量的析构函数;3、执行父类的析构函数。依旧是在上面的程序基础之上,来看看析构函数的执行顺序。

#include <iostream>
#include <string>

using namespace std;

class Object
{
    string ms;
public:
    Object(string s)
    {
        ms  = s;
        
        cout << "Object(string s): " << ms << endl;
    }
    
    ~Object()
    {
        cout << "~Object() : " << ms << endl;
    }
};

class Parent : public Object
{
    string ms;
public:
    Parent() : Object("Default")
    {
        ms = "Default";
    
        cout << "Parent()" << endl;
    }

    Parent(string s) : Object(s)
    {
        ms = s;
    
        cout << "Parent(string s): " << s << endl;
    }
    
    ~Parent()
    {
        cout << "~Parent() : " << ms << endl;
    }
};

class Child : public Parent
{
    Object mOb1;
    Object mOb2;
    string ms;
public:
    Child() : mOb1("Default 1"), mOb2("Default 2")
    {
        ms = "Default";
    
        cout << "Child()" << endl;
    }
    
    Child(string s) : Parent(s), mOb1(s + " 1"), mOb2(s + " 2")
    {
        ms = s;
        
        cout << "Child(string s): " << s << endl;
    }
    
    ~Child()
    {
        cout << "~Child() : " << ms << endl;
    }
};

int main()
{
    Child cc("cc");
    
    return 0;
}

        我们来看看编译结果

技术分享图片

        通过对继承里的构造与析构函数的学习,总结如下:1、子类对象在创建时需要调用父类构造函数进行初始化;2、先执行父类构造函数然后执行成员的构造函数;3、父类构造函数显示调用需要在初始化列表中进行;4、子类对象在销毁时需要调用父类析构函数进行清理;5、析构顺序与构造顺序对称相反。


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

继承中的构造与析构(三十九)

标签:C++   继承   构造顺序   析构顺序   初始化列表   

原文地址:http://blog.51cto.com/12810168/2120704

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!