C++--继承中的构造与析构父子间的冲突

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++--继承中的构造与析构父子间的冲突相关的知识,希望对你有一定的参考价值。

一.继承中的构造与析构

Q:如何初始化父类成员?父类构造函数与子类构造函数由什么关系?
A.子类对象的构造
1.子类在可以定义构造函数
2.子类构造函数--必须对继承而来的成员进程初始化(直接通过初始化列表或者赋值的方式进行初始化,调用父类构造函数进行初始化)
B.父类构造函数在子类中的调用方式
1.默认调用--适用于无参构造函数和使用默认参数的构造函数
2.显示调用--通过初始化列表进行调用,适用于所有父类构造函数
代码示例

#include <iostream>
#include <string>

using namespace std;

class Parent 

public:
    Parent()
    
        cout << "Parent()" << endl;
    
    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;

运行结果如图所示
技术图片
由该示例和运行结果可以对构造规则进行一个总结:子类对象在创建时首先会调用父类的构造函数,先执行父类构造函数在执行子类的构造函数,父类构造函数可以被隐式调用或者显示调用
子类对象的构造:1.调用父类的构造函数2.调用成员变量的构造函数3.调用类自身的构造函数
子类构造深度解析

#include <iostream>
#include <string>

using namespace std;

class Object

public:
    Object(string s)
    
        cout << "Object(string s) : " << s << endl;
    
;

class Parent : public Object

public:
    Parent() : Object("Default")
    
        cout << "Parent()" << endl;
    
    Parent(string s) : Object(s)
    
        cout << "Parent(string s) : " << s << endl;
    
;

class Child : public Parent

    Object mO1;
    Object mO2;
public:
    Child() : mO1("Default 1"), mO2("Default 2")
    
        cout << "Child()" << endl;
    
    Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2")
    
        cout << "Child(string s) : " << s << endl;
    
;

int main()
       
    Child cc("cc");

    return 0;

运行结果
技术图片
Object mO1 Object mO2是组合关系,要对父类进行显式的调用,该打印结果与上面所提的构造顺序是相符的
C.子类对象的析构
析构函数的调用顺序与构造函数相反
1.执行自身的析构函数
2.执行成员变量的析构函数
3.执行父类的析构函数

代码示例

#include <iostream>
#include <string>

using namespace std;

class Object

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

class Parent : public Object

    string ms;
public:
    Parent() : Object("Default")
    
        cout << "Parent()" << endl;
        ms = "Default";
    
    Parent(string s) : Object(s)
    
        cout << "Parent(string s) : " << s << endl;
        ms = s;
    
    ~Parent()
    
        cout << "~Parent() : " << ms << endl;
    
;

class Child : public Parent

    Object mO1;
    Object mO2;
    string ms;
public:
    Child() : mO1("Default 1"), mO2("Default 2")
    
        cout << "Child()" << endl;
        ms = "Default";
    
    Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2")
    
        cout << "Child(string s) : " << s << endl;
        ms = s;
    
    ~Child()
    
        cout << "~Child() " << ms << endl;
    
;

int main()
       
    Child cc("cc");

    cout << endl;

    return 0;

运行结果
技术图片
小结:
1.子类对象在创建时需要调用父类构造函数进行初始化
2.先执行父类构造函数然后执行成员的构造函数
3.父类构造函数显示调用需要在初始化列表中进行
4.子类对象在销毁时需要调用父类析构函数进行清理
5.析构函数与构造函数对称相反

二.父子间的冲突

Q:子类中是否可以定义父类的同名成员?如果可以?怎样区分?如果不行,为什么?
代码示例

#include <iostream>
#include <string>

using namespace std;

class Parent

public:
    int mi;
;

class Child : public Parent

public:
    int mi;
;

int main()

    Child c;

    c.mi = 100;    // mi 究竟是子类自定义的,还是从父类继承得到的?
    cout<<"c.mi="<<c.mi<<endl; 
    return 0;

运行结果
技术图片
从该示例以及运行结果我们会得出一些疑问,根据继承的概念,子类拥有父类的所有属性和行为,在该程序中父类与子类都定义了mi,而在其初始化时,不确定是子类自定义的,还是从父类继承得到的。造成了父子间的冲突。
A.父子间的冲突
1.子类可以定义父类中的同名成员
2.子类中的成员将隐藏父类中的同名成员
3.父类的中的同名成员依然存在于子类中
4.通过作用域分辨符(::)访问父类的同名成员
技术图片
同名成员变量深度分析--代码示例

#include <iostream>
#include <string>

using namespace std;

namespace A

    int g_i = 0;


namespace B

    int g_i = 1;


class Parent

public:
    int mi;

    Parent()
    
        cout << "Parent() : " << "&mi = " << &mi << endl;
    
;

class Child : public Parent

public:
    int mi;

    Child()
    
        cout << "Child() : " << "&mi = " << &mi << endl;
    
;

int main()

    Child c;

    c.mi = 100;    

    c.Parent::mi = 1000;

    cout << "&c.mi = " << &c.mi << endl;
    cout << "c.mi = " << c.mi << endl;

    cout << "&c.Parent::mi = " << &c.Parent::mi << endl;
    cout << "c.Parent::mi = " << c.Parent::mi << endl;

    return 0;

运行结果
技术图片
可以从该段代码的运行结果看出,通过地址值可以很清楚得看出,父类与子类到底调用的是哪个
B:再论重载
类中的成员函数可以进行重载
1.重载函数的本质为多个不同的函数
2.函数名域参数列表是唯一的标识
3.函数重载必须发生在同一作用域中

父子间函数重载实验
代码示例及运行结果

#include <iostream>
#include <string>

using namespace std;

class Parent

public:
    int mi;

    void add(int v)
    
        mi += v;
    

    void add(int a, int b)
    
        mi += (a + b);
    
;

class Child : public Parent

public:
    int mi;

    void add(int v)
    
        mi += v;
    

    void add(int a, int b)
    
        mi += (a + b);
    

    void add(int x, int y, int z)
    
        mi += (x + y + z);
    
;

int main()

    Child c;

    c.mi = 100;    

    c.Parent::mi = 1000;

    cout << "c.mi = " << c.mi << endl;

    cout << "c.Parent::mi = " << c.Parent::mi << endl;

    c.add(1);
    c.add(2, 3);
    c.add(4, 5, 6);

    cout << "c.mi = " << c.mi << endl;

    cout << "c.Parent::mi = " << c.Parent::mi << endl;

    return 0;

技术图片
父子间的冲突
1.子类中的函数将隐藏父类的同名函数
2.子类无法重载父类中的成员函数
3.使用作用域分辨符访问父类中的同名函数
4.子类可以定义父类中完全相同的成员函数

以上是关于C++--继承中的构造与析构父子间的冲突的主要内容,如果未能解决你的问题,请参考以下文章

继承中的构造与析构

C++ 类的继承三(继承中的构造与析构)

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

第46课 继承中的构造与析构

第46课 继承中的构造与析构

第四十六课继承中的构造与析构