继承的好处,可以减少代码的重复
class A : 继承方式 B;
A类为子类,B类为父类
子类的成员分别由从父类继承来的成员 和自己本身的成员构成。
继承方式有public,protect,private 三种
首先 不管是哪种继承方式,子类都无法继承父类的私有成员,并非没有继承,而是私有成员被隐藏。
如果是公有继承,则子类直接继承父类中除了私有的成员,并且不做任何变化
如果是保护继承,父类中除了私有的成员在子类中全部变成保护权限(保护权限 类外不可以访问)
如果是私有继承,则全部变成私有权限
继承中的对象模型
cl /d1 reportSingleClassLayout类名 文件名.cpp 可以在命令行中查看类的结构
子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承中的构造和析构的顺序如下,先构成父类再构成子类,析构顺序反过来
继承同名成员处理方式
如果通过子类对象访问父类的同名成员,需要加入作用域 s.father::age
多继承语法
允许一个类继承多个类
语法: class 子类: 继承方式 父类1, 继承方式 父类2,。。。
多继承可能引发父类中有同名成员的出现, 因此要室友作用域加以区分
菱形继承
两个子类同时继承一个父类,又有某个子类同时继承两个子类,这种继承被称为菱形继承
比如 动物 - 羊,驼-羊驼, 因为羊驼继承了羊和驼,那么数据会产生二义性
利用虚继承解决菱形继承问题, 在继承类型前加上关键字virtual 变成虚继承
多态
多态分为静态多态和动态多态
静态多态:函数重载和运算符重载都是静态多态,复用函数名
动态多态: 子函数和虚函数实现运行时多态
区别:
静态多态的函数地址早绑定,即编译阶段确定函数地址
动态多态的函数地址晚绑定,运行阶段才确定函数地址
class Animal {
public:
virtual void speak() {
cout << "animal speak chinese" << endl;
}
};
class Cat : public Animal {
public:
void speak() {
cout << "cat speak meow" << endl;
}
};
void doSpeak(Animal* animal) { // 在c++中,父类的指针可以直接指向子类
animal->speak();
}
int main() {
Cat c;
c.speak();
doSpeak(&c); //执行的是动物在说话,因为地址早绑定了 如果想要地址晚绑定 在说话函数前加上virtual
}
动态多态满足条件
1.有继承关系
2.子类要重写父类的虚函数
多态原理
类中含有virtual 成员函数,即便不再含有其他变量和函数,该类仍然占有4字节(空类只占1字节),原因是virtual 成员函数会给类添加一个virtual function ptr, 该指针指向一个列表,里面记录着虚函数的地址,当子类继承时,也会继承该指针,并指向子类的虚函数的地址,而子类的虚函数和父类的不一样,因此,当出现Animal & animal = cat 时,此时虚函数列表为cat的列表,故而animal.speak访问的是cat的speak。
在多态中,通常父类中虚函数的实现是毫无意义的,主要调用子类重写的内容,因此可以将虚函数改成纯虚函数
纯虚函数写法:
virtual 返回值类型 函数名(参数列表) = 0;
当类中有了虚函数,这个类也称为抽象类
抽象类的特点:无法实例化对象,且子类必须重写抽象类的纯虚函数,否则子类也是抽象类
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在析构时无法调用到子类的析构代码
解决方式:将父类的析构函数改成虚析构和纯虚析构
虚析构和纯虚析构的共性:可以解决父类指针释放子类对象,都需要具体的函数实现
区别: 如果是纯虚析构,那么该类属于抽象类,无法实例化对象。
虚析构和纯虚析构都需要声明 和代码实现, 但如果子类中没有堆区数据,可以不写虚析构或者纯虚析构
示例代码:
#include "base.h"
class Animal {
public:
Animal() {
cout << "Animal 构造函数" << endl;
}
virtual ~Animal() = 0;
virtual void speak() = 0;
};
Animal :: ~Animal() {
cout << "Animal 析构" << endl;
}
class Cat:public Animal {
public:
string* name;
Cat(string nom) {
cout << "Cat 构造函数" << endl;
name = new string(nom);
}
void speak() {
cout << *this->name << " is speaking" << endl;
}
~Cat() {
cout << "Cat 析构函数" << endl;
if (this->name != NULL) {
delete name;
name = NULL;
}
}
};
int main() {
Animal* animal = new Cat("tom");
animal->speak();
delete animal;
}