0
点赞
收藏
分享

微信扫一扫

【C++grammar】继承与构造


目录

  • ​​1.继承​​
  • ​​1、Inheritance (继承)​​
  • ​​2、避免一个类被继承( C++11 )​​
  • ​​3、继承实例​​
  • ​​4、完整代码​​
  • ​​5、继承的优缺点是什么?​​
  • ​​2.继承中的构造函数​​
  • ​​1、 派生类继承的成员​​
  • ​​2、调用基类构造函数​​
  • ​​3.继承中的默认构造函数​​
  • ​​1、基类的无参构造函数​​
  • ​​2、由编译器自动生成的基类构造函数会不会被派生类继承?​​
  • ​​4. 构造链和析构链​​
  • ​​1、构造函数链​​
  • ​​2、析构函数链​​
  • ​​3、例1​​
  • ​​4、例2​​
  • ​​5、调用所有的构造函数和析构函数​​


1.继承

1、Inheritance (继承)

【C++grammar】继承与构造_继承
【C++grammar】继承与构造_类_02

2、避免一个类被继承( C++11 )

class B final {};
class D : public B {};

编译后的输出是 (Visual Studio)
error C3246: “D”: 无法从“B”继承,因为它已被声明为“final”这是程序输出

3、继承实例

【C++grammar】继承与构造_desktop_03

1、创建rectangle类、Circle类,从shape类继承。

class Rectangle : public Shape {......};
class Circle: public Shape {......};

2、使用基类的构造函数初始化基类的数据成员
继承之后,rectangle也获得了shape的属性和方法,这里修改rectangle类的有参初始化函数,同时也对继承过来的基类数据成员进行初始化。

Circle::Circle(double radius_, Color color_, bool filled_) : Shape{color_,filled_} {
radius = radius_;
}
Rectangle::Rectangle(double w, double h, Color color_, bool filled_) :width{ w }, height{h}, Shape{ color_,filled_ } {}

4、完整代码

继承与构造test1代码附录

5、继承的优缺点是什么?

继承方便程序员使用他人写好的类来实现自己的功能,而不用重复造轮子。
坏处是有时继承过程中会继承到许多无用的信息,而且增加了程序的复杂性,容易出错。

2.继承中的构造函数

1、 派生类继承的成员

C++11:派生类不继承的特殊函数:

(1) 析构函数
(2) 友元函数

继承基类构造函数:

(1) using A::A; 继承所有基类ctor
(2) 不能仅继承指定的某个基类ctor

调用继承的构造函数

struct A { // 等价于 class A { public:
A(int i) {}
A(double d, int i) {}
// ...
};

struct B : A { // C++11
using A::A; // 继承基类所有构造函数
int d{ 0 }; // 就地初始化
};

int main() {
B b(1); // 调A(int i)
}

2、调用基类构造函数

若派生类成员也需要初始化,则可以在派生类构造函数中调用基类构造函数。
调用次序:
先调用基类构造函数,再调用内嵌对象构造函数,最后再执行函数体

#include<iostream>
using std::cout;
using std::endl;

//task1:继承构造函数
//创建基类B及构造函数B(int),B(char)和派生类D;
//D中不继承/继承B的ctor时的效果。

//task2:派生类中调用基类构造函数
//D中增加成员double x;及D(double),在D(double)初始化列表调用B(i)并初始化x

//task3:派生类先调用基类ctor,再构造内嵌对象
//增加类E及E(int) ,并在D中加入E的两个对象;创建D对象观察E ctor和B ctor 次序

class B {
public:
B() { cout << "B()" << endl; }
B(int i) { cout << "B("<< i << ")" << endl; }
B(char c) { cout << "B(" << c << ")" << endl; }
};
class E {
public:
E() { cout << "E()" << endl; }
};

//D里面没有构造函数,所以编译器会自动生成一个默认构造函数,去调用基类的默认构造函数
class D :public B {
private:
//double x{ 0.0 };
E e1, e2;
public:
//继承构造函数
using B::B;
D() = default; //显示声明
//D(int i):B(i){}
//D(char c):B(c){}
//先调用基类构造函数,再调用内嵌对象构造函数,最后再执行函数体
D(double x) :e1{}, e2{}, B(static_cast<int>(x)){ cout << "D(" << x << ")" << endl;}
};

int main()
{
B b;
D d;
D d2{3.02};
}

当程序员在派生类构造函数中显式调用基类构造函数时,应将被调用基类构造函数放在派生类构造函数初始化列表中。

3.继承中的默认构造函数

1、基类的无参构造函数

若基类ctor未被显式调用,基类的默认构造函数就会被调用。
所以这种继承的,一定要小心地书写基类的默认构造函数。
【C++grammar】继承与构造_类_04

2、由编译器自动生成的基类构造函数会不会被派生类继承?

class A {};
class B: public A {
public:
using A::A;
}

编译器为基类生成的默认构造函数会不会被继承到类B中?
在B中提供有参构造函数使编译器不生成默认构造函数,这段代码可以执行成功。所以在没有其他机制的情况下,暂且认为编译器生成的A类默认构造函数被B类继承了。
​C++编译器生成默认的构造函数的几种情况

4. 构造链和析构链

1、构造函数链

构造类实例会沿着继承链调用所有的基类ctor。
调用次序: base first, derive next (父先子后)。

2、析构函数链

dtor与ctor正好相反。
调用次序: derive first, base next (子先父后)

3、例1

任务1:创建类结构:Computer->PC->Desktop/Laptop以及相应的ctor/dtor
main中创建Desktop/Laptop的对象,观察ctor/dtor调用次序

#include <iostream>
using std::cout;
using std::endl;

//任务1:创建类结构:Computer->PC->Desktop/Laptop以及相应的ctor/dtor
// main中创建Desktop/Laptop的对象,观察ctor/dtor调用次序
class Computer {
public:
Computer() { cout << "Computer()" << endl; }
~Computer() { cout << "~Computer()" << endl; }
};
class PC : public Computer {
public:
PC() { cout << "PC()" << endl; }
~PC() { cout << "~PC()" << endl; }
};

class Desktop : public PC {
public:
Desktop() { cout << "Desktop()" << endl; }
~Desktop() { cout << "~Desktop()" << endl; }
};

class Laptop : public PC {
public:
Laptop() { cout << "Laptop()" << endl; }
~Laptop() { cout << "~Laptop()" << endl; }
};
int main()
{
Desktop d();
Laptop l();
return 0;
}

效果:
【C++grammar】继承与构造_继承_05

4、例2

任务2:增加类Camera作为Laptop的内嵌对象c的类型
main中创建Laptop对象,观察内嵌对象c的构造与基类构造次序

#include <iostream>
using std::cout;
using std::endl;
//任务2:增加类Camera作为Laptop的内嵌对象c的类型
// main中创建Laptop对象,观察内嵌对象c的构造与基类构造次序


class Computer {
public:
Computer() { cout << "Computer()" << endl; }
~Computer() { cout << "~Computer()" << endl; }
};
class PC : public Computer {
public:
PC() { cout << "PC()" << endl; }
~PC() { cout << "~PC()" << endl; }
};

class Desktop : public PC {
public:
Desktop() { cout << "Desktop()" << endl; }
~Desktop() { cout << "~Desktop()" << endl; }
};
class Camera {
public:
Camera() { cout << "Camera()" << endl; }
~Camera() { cout << "~Camera()" << endl; }
};
class Laptop : public PC {
private:
Camera c{};
public:
Laptop() { cout << "Laptop()" << endl; }
~Laptop() { cout << "~Laptop()" << endl; }
};
int main()
{
Desktop d{};
Laptop l{};
return 0;
}

效果:
【C++grammar】继承与构造_desktop_06
构造过程:在祖先类的构造函数执行完后,执行内嵌对象的构造函数,最后执行自己的构造函数。
析构过程:与继承链相反。

5、调用所有的构造函数和析构函数

我们知道,一个类可能有不止一个构造函数。
那么,能否写出一个例子,创建一个派生类对象,从而把单继承链(链无分支)上的所有类的所有构造函数和析构函数都调一遍?
使用代理构造:​【C++grammar】代理构造、不可变对象、静态成员

#include <iostream>
using std::cout;
using std::endl;
class A {
public:
A() : A(0) { cout << "A()" << endl; }
A(int i) : A(i, 0) { cout << "A(int i)" << endl; }
A(int i, int j) {
num1 = i;
num2 = j;
average = (num1 + num2) / 2;
cout << "A(int i, int j)" << endl;
}
private:
int num1;
int num2;
int average;
};
class A_child : public A {
public:
A_child() { cout << "A_child()" << endl; }
~A_child() { cout << "~A_child()" << endl; }
};
int main()
{
A_child l{};
return 0;
}

【C++grammar】继承与构造_类_07


举报

相关推荐

0 条评论