18.面向对象-继承-继承后的构造函数和析构函数
18.1 知识点
继承中的构造函数
执行顺序
当子类实例化(进行构造)时,构造函数的执行顺序如下:
- 先执行父类构造函数(按继承顺序)
- 再执行子类中成员对象的构造函数(按声明顺序)
- 最后执行子类自身的构造函数体
可总结为:先父后子内,子内结束最后子
实际观察一个例子,可以发现:
首先执行父类构造函数
然后是子类中成员对象的构造函数
最后才是子类本身的构造函数体多重继承情况下也是如此。
即:有祖父类 → 父类再加一个成员对象,构造时依然满足以上规则。
基类构造的调用方式
默认情况下,子类构造函数会自动调用父类的无参构造函数
如果父类没有无参构造函数,子类必须显式调用父类的带参构造函数
否则会出现编译错误举例说明:
- 假设祖父类的构造函数有参数,顶掉了默认无参构造函数
- 那么在父类构造函数中必须显式调用祖父类构造传参
- 否则在子类中也会因为找不到合适的祖先构造方式而报错
继承中的析构函数
执行顺序
当子类被销毁(进行析构)时,析构函数的执行顺序如下:
- 先执行子类析构函数
- 再析构子类成员对象
- 最后执行父类析构函数
可总结为:先子后子内,子内结束最后父
实例
构造函数和析构函数不能被继承
构造函数和析构函数不会被子类继承
举例:
s.Test(); // 这是一个普通函数,在父类实现,子类可以直接调用 // Son s2 = Son(10); // 报错:因为父类中有有参构造,而子类没有提供构造函数 // 构造函数不能被继承,因此不能直接使用父类的构造方式创建子类对象
如果希望在子类中使用父类构造函数,可以使用
using
显式引入父类的构造函数:using 父类名::父类名;
它的作用是让子类中拥有与父类相同的构造函数
注意事项:
using
的方式仅适用于构造函数- 析构函数不适用该方式,因为析构函数规则是固定的,且不带参数
实例
总结
构造函数与析构函数在继承中的执行顺序:
- 构造时顺序:父类 → 子类成员对象 → 子类本身
- 析构时顺序:子类 → 子类成员对象 → 父类
默认调用的父类构造函数是无参构造
如果父类中没有无参构造,则需要子类中显式调用带参构造函数构造函数和析构函数不能被继承
- 若要在子类中使用和父类一样的构造函数
必须使用using 父类名::父类名
引入
- 若要在子类中使用和父类一样的构造函数
18.2 知识点代码
GrandPa.h
#pragma once
#include <iostream>
using namespace std;
class GrandPa
{
public:
GrandPa(int i)
{
cout << "GrandPa构造" << i << endl;
}
~GrandPa()
{
cout << "GrandPa析构" << endl;
}
};
Father.h
#pragma once
#include "GrandPa.h"
#include "Other.h"
class Father : public GrandPa
{
public:
Other other1;
Father(int i) : GrandPa(i)
{
cout << "Father1个参数的构造" << endl;
}
Father(int i, int j) : GrandPa(i)
{
cout << "Father2个参数的构造" << endl;
}
~Father()
{
cout << "Father析构" << endl;
}
void Test()
{
}
};
Son.h
#pragma once
#include "Father.h"
#include "Other.h"
class Son : public Father
{
public:
Other other2;
using Father::Father;
Son() : Father(99)
{
cout << "Son构造" << endl;
}
~Son()
{
cout << "Son析构" << endl;
}
};
Other.h
#pragma once
#include <iostream>
using namespace std;
class Other
{
public:
Other()
{
cout << "Other构造" << endl;
}
~Other()
{
cout << "Other析构" << endl;
}
};
Lesson18_面向对象_继承_继承后的构造函数和析构函数.cpp
#include <iostream>
#include "Son.h"
int main()
{
#pragma region 知识点一 继承中的构造函数
#pragma region 执行顺序
//在子类进行构造时(实例化时),构造函数的执行顺序是
//1.按照继承顺序,先执行父类构造函数
//2.再按照声明顺序 执行成员对象的构造函数
//3.最后执行子类自己的构造函数体
//先父后子内,子内结束最后子
Son s;
#pragma endregion
#pragma region 基类构造的调用方式
//在自动执行父类构造函数时,默认调用的是父类的无参构造函数
//如果父类没有无参构造函数,子类必须显示调用带参构造函数
//否则会报错
#pragma endregion
#pragma endregion
#pragma region 知识点二 继承中的析构函数
#pragma region 执行顺序
//在子类进行析构时(销毁时),析构函数的执行顺序是
//1.先执行子类析构
//2.再析构成员对象
//3.最后析构父类对象
//先子后子内,子内结束最后父
#pragma endregion
#pragma endregion
#pragma region 知识点三 构造函数和析构函数不能被继承
//构造函数和析构函数不会被子类继承
//比如:父类中有一个有参构造函数
// 但是我们在子类中是不能使用的
s.Test();//这个Test是普通函数 是在父类实现的 子类可以随便调
//Son s2 = Son(10);//报错
//父类中有一个有参构造函数 子类没有有参构造函数 会报错 因为构造函数不能被继承
//如果我们想要在子类中能够直接使用父类中的构造
//我们可以通过using关键字,显示的引入基类的构造函数
// 语法:using 父类名::父类名;
//它的作用相当于让子类中多了和父类一模一样的构造函数
//注意:只针对构造函数而言,析构函数不存在这样的做法,因为析构函数的规则是固定的,无参
//在子类添加了 using Father::Father; 可以使用父类父类的有参构造函数了
Son s2 = Son(10);
Son s3 = Son(1, 3);
#pragma endregion
//总结
//1.构造函数和析构函数在继承中的执行顺序
// 父-子内-子 子-子内-父
//2.默认调用的父类的构造是无参构造,如果父类中没有了,需要显示调用其他构造
//3.构造函数和析构函数不能被继承
// 如果想要在子类中使用和父类结构一样的构造函数 需要使用 using 父类名::父类名
}
18.3 练习题
有一个打工人基类,有工种、工作内容两个特征,一个工作方法;程序员、策划、美术分别继承打工人,请用继承中的构造函数知识点实例化三种对象
有一个基类 Worker
,包含两个成员变量 type
(工种)和 content
(工作内容),以及一个成员方法 Work()
。
三种子类 Programmer
、Plan
、Art
分别在其构造函数中调用基类构造函数,传入对应的工种和工作内容。
#pragma once
#include <iostream>
using namespace std;
class Worker
{
public:
string type; // 工种
string content; // 工作内容
Worker(string type, string content) : type(type), content(content) {};
void Work();
};
#include "Worker.h"
void Worker::Work()
{
cout << "职位:" <
#pragma once
#include "Worker.h"
class Art : public Worker
{
public:
Art() : Worker("美术", "画图建模设计UI")
{
}
};
< type << " 工作内容:" << content << endl;
}
#pragma once
#include "Worker.h"
class Plan : public Worker
{
public:
Plan() : Worker("策划", "设计游戏")
{
}
};
#pragma once
#include "Worker.h"
class Programmer : public Worker
{
public:
Programmer() : Worker("程序员", "编程")
{
}
};
Programmer programmer;
programmer.Work();
Plan plan;
plan.Work();
Art art;
art.Work();
// 输出:
// 职位:程序员 工作内容:编程
// 职位:策划 工作内容:设计游戏
// 职位:美术 工作内容:画图建模设计UI
18.4 练习题代码
Worker.h
#pragma once
#include <iostream>
using namespace std;
class Worker
{
public:
string type;//工种
string content;//内容
Worker(string type, string content) : type(type), content(content) {};
void Work();
};
Worker.cpp
#include "Worker.h"
void Worker::Work()
{
cout << "职位:" << type << " 工作内容:" << content << endl;
}
Art.h
#pragma once
#include "Worker.h"
class Art : public Worker
{
public:
Art() : Worker("美术", "画图建模设计UI")
{
}
};
Plan.h
#pragma once
#include "Worker.h"
class Plan : public Worker
{
public:
Plan() : Worker("策划", "设计游戏")
{
}
};
Programmer.h
#pragma once
#include "Worker.h"
class Programmer : public Worker
{
public:
Programmer() : Worker("程序员", "编程")
{
}
};
Lesson18_练习题.cpp
#include <iostream>
#include "Programmer.h"
#include "Plan.h"
#include "Art.h"
int main()
{
#pragma region 练习题
/*有一个打工人基类,有工种,工作内容两个特征,一个工作方法
程序员、策划、美术分别继承打工人
请用继承中的构造函数这个知识点
实例化3个对象,分别是程序员、策划、美术*/
Programmer programmer;
programmer.Work();
Plan plan;
plan.Work();
Art art;
art.Work();
//职位:程序员 工作内容:编程
//职位:策划 工作内容:设计游戏
//职位:美术 工作内容:画图建模设计UI
#pragma endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com