标准C++语言-类的继承Ⅱ

一、子类的构造、析构、拷贝

  • 子类的构造
    • 执行子类的构造函数前会根据继承表的顺序执行父类的构造函数
    • 默认执行父类的无参构造
    • 显式调用有参构造,在子类的构造函数后,初始化列表中显式调用父类的有参构造函数
  • 子类的析构
    • 子类的析构函数执行后,会根据继承表的顺序逆序执行父类的析构函数
    • 注意:父类的指针可以指向子类对象,当通过父类指针释放对象时,只会调用父类的析构函数,而这种析构方式有可能造成内存泄漏
  • 子类的拷贝
    • 当使用子类对象来初始化新的子类对象时,会自动调用子类缺省的拷贝构造函数,并且会先调用父类缺省的拷贝构造函数
    • 子类中实现的拷贝构造,需要显式调用父类拷贝构造,否则就会调用无参构造

二、私有继承、保护继承

  • 使用private方式继承父类
    • 公有的变成私有的,其他的不变,这种继承方式防止父类的成员扩散
  • 使用 protected 方式继承父类
    • 公有的变成保护的,其他的不变,这种继承方式可以防止父类的成员扩散
  • 子类以私有或保护方式继承父类,禁止向上造型
    • 子类的指针或引用不能隐式转换成父类的指针或引用,要想实现多态只能以公开方式继承父类

三、多重继承、钻石继承、虚继承

  • 多重载继承
    • C++中一个子类可以有多个父类,在继承表中按照顺序继承多个父类的属性和行为,并按照顺序表中的调用父类的构造函数
    • 按照从低到高的地址顺序排列父类,子类中会标记每个父类存储位置
    • 当子类指针转换成父类的隐式指针时,编译器会自动计算父类中的内存所在子类中的位置,地址会自动进行偏移计算
  • 名字冲突
    • 如果父类中有同名的成员,可以正常继承
    • 但如果直接使用,会造成歧义,需要 类名::成员名 进行访问
  • 钻石继承
    • 例如:一个类A,类B继承类A,类C也继承类A,然后类D继承类B和类C
    • 例如:一个子类继承多个父类,这些父类有一个共同的祖先
    • 注意:钻石继承不会导致继承错误,但访问祖先类中的成员时每次需要使用 类名::成员名这种继承会造成冗余
  • 虚继承virtual
    • 当进行钻石继承时,祖先类中的内容会有冗余,而进行虚继承后,在子类中的内容和构造函数只会保留一份
    • 注意:但使用虚继承时子类中会多了一些内容(指向从祖先类继承来的成员)
  • 构造函数
    • 一旦进行了虚继承(钻石继承),祖先类的构造函数只执行一次,由孙子类直接调用,祖先类的有参构造也需要在孙子类中显式调用
  • 拷贝构造
    • 在虚继承(钻石继承)中祖先的的拷贝构造由孙子类直接调用,子类中不再调用祖先的拷贝构造,在手动实现的拷贝构造时(深拷贝),祖先类中的内容也由孙子类负责拷贝
    • 同理赋值构造也一样

四、虚函数、覆盖、多态

  • 虚函数
    • 类的成员函数前加 virtual
  • 覆盖
    • 子类会覆盖父类的虚函数
  • 多态
    • 当子类覆盖了父类的虚函数时,通过父类指针指向子类对象时,调用虚函数,会根据具体的对象是谁来决定执行谁的函数

五、覆盖和多态的条件

  • 覆盖的条件
    • 必须是虚函数
    • 必须在父子类之间
    • 函数签名必须相同(参数列表完全一致 const 需要一致
    • 返回值必须是同类或者父子类(子类的返回值要能向父类的隐式转换
    • 访问属性不会影响覆盖
    • 常函数属性影响覆盖
  • 重载、隐藏、覆盖(重写)的区别
    • 重载:发生在同一作用域下的同名函数,参数不同,函数签名不同,构成重载
    • 隐藏:发生在父子不同作用域下的同名成员,如果没有形成覆盖,且能通过编译,必定是隐藏
    • 覆盖(重写):发生在父子不同作用域下的虚函数修饰的父类函数
  • 多态的条件
    • 父子类之间有的函数有覆盖关系
    • 父类的指针或引用指向子类的对象
    • 注意:在父类的构造函数中调用虚函数,此时子类还没有创建完成,因此只能调用父类的虚函数,而不是覆盖版本的虚函数

六、纯虚函数和抽象类

  • 纯虚函数

    • 在纯虚函数的声明的后面添加=0,不可实现
    • 用法virtual 返回值 函数名(参数) = 0
    • 注意一定要实现的话,只能在类外部实现,且在父类的构造函数和析构函数中才能调用
  • 抽象类

    • 抽象类的成员函数中有纯虚函数
    • 抽象类不能实例化(不能创建对象)
    • 抽象类被继承,纯虚函数被覆盖,由子类实例化对象
    • 若子类没有覆盖纯虚函数,子类也将成为抽象类,也不能实例化
  • 纯抽象类

    • 纯抽象类的所有成员函数都是纯虚函数
    • 这种类一般用来设计接口,这种类在子类被替换后不需要修改或少量的修改即可继续使用
  • 纯抽象类的应用工厂类模式

#include <iostream>
using namespace std;

class Base{
public:
	virtual void show(void) = 0;
};
class A:public Base{
public:
	void show(void){
		cout << "我是类A的show函数" << endl;
	}
};
class B:public Base{
public:
	void show(void){
		cout << "我是类B的show函数" << endl;
	}
};
class C:public Base{
public:
	void show(void){
		cout << "我是类C的show函数" << endl;
	}
};
enum ClassType{typeA,typeB,typeC};

//大名鼎鼎的工厂类模式
Base* creat_object(ClassType type){
	switch(type)	{
		case typeA: return new A;
		case typeB: return new B;
		case typeC: return new C;
		default: return NULL;
	}
}
int main(){
	Base* p = creat_object(typeC);
	p->show();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值