一、菱形继承
又叫钻石继承,由公共子类派生出多个中间子类,又由多个中间子类派生出汇聚子类,汇聚子类会
从中间子类得到从公共基类继承下来的多个成员。
A --------公共基类/ \B C ------- 中间子类\ /D --------汇聚子类
#include <iostream>using namespace std;//封装公共基类 家具 类
class Jiaju
{
private:string color;
public://无参构造Jiaju() {cout << "家具的无参构造函数" << endl;}//有参构造Jiaju(string n):color(n){cout << "家具的有参构造函数" << endl;}
};//中间子类
//封装 沙发的类
class Sofa:public Jiaju
{
private:string sitting;
public://无参构造Sofa() {cout << "沙发的无参构造" << endl;}//有参构造函数Sofa(string s,string c):Jiaju(c),sitting(s){cout << "沙发的有参构造" << endl;}void display(){cout << sitting << endl;}
};//中间子类
//封装 床 类
class Bed:public Jiaju
{
private:string sleep;public://无参Bed() {cout << "床的无参构造" << endl;}//有参Bed(string s,string c):Jiaju(c),sleep(s){cout << "床的有参构造" << endl;}void display(){cout << sleep << endl;}
};//汇聚子类
//封装 沙发床类 继承于沙发 和 床
class Sofa_Bed:public Bed,public Sofa
{
private:int w;
public://Sofa_Bed(){cout << "沙发床的无参构造" << endl;}//有参构造Sofa_Bed(string sit, string s, int w,string c):Bed(s,c),Sofa(sit,c),w(w){cout << "沙发床的有参构造" << endl;}
};int main()
{
// Sofa_Bed s;Sofa_Bed s1("可坐","可躺",123,"pink");return 0;
}
由于汇聚子类会得到中间子类从公共基类继承下来的多份基类成员,故引出了虚继承的概念
二、虚继承
为了使汇聚子类只保存一份中间子类从公共基类继承的成员,使用了在中间子类继承方式前virtual
class 类名 : virtual 继承方式 类名 //中间子类
{中间子类的拓展;
};
1.默认调用公共基类的无参构造函数
当你创建一个汇聚子类的对象时,编译器需要确定如何初始化公共基类。由于虚继承,公共基类只有一个实例,因此需要明确哪个构造函数应该被调用。
如果你没有在汇聚子类的构造函数初始化列表中显式调用公共基类的构造函数,编译器会默认调用公共基类的无参构造函数。这是因为编译器不知道应该选择哪个中间子类的构造函数来初始化公共基类,所以它选择最安全的选项,即调用无参构造函数。
2.显式调用公共基类的构造函数
如果你想在创建汇聚子类的对象时初始化公共基类的数据成员,你需要在汇聚子类的构造函数初始化列表中显式调用公共基类的构造函数。
这样做可以确保公共基类按照你的需求进行初始化,而不是使用默认的无参构造函数。
#include <iostream>using namespace std;//封装公共基类 家具 类
class Jiaju
{
private:string color;
public://无参构造Jiaju() {cout << "家具的无参构造函数" << endl;}//有参构造Jiaju(string n):color(n){cout << "家具的有参构造函数" << endl;}
};//中间子类
//封装 沙发的类
class Sofa:virtual public Jiaju //中间子类虚继承公共基类
{
private:string sitting;
public://无参构造Sofa() {cout << "沙发的无参构造" << endl;}//有参构造函数Sofa(string s,string c):Jiaju(c),sitting(s){cout << "沙发的有参构造" << endl;}void display(){cout << sitting << endl;}
};//中间子类
//封装 床 类
class Bed:virtual public Jiaju //中间子类虚继承公共基类
{
private:string sleep;public://无参Bed() {cout << "床的无参构造" << endl;}//有参Bed(string s,string c):Jiaju(c),sleep(s){cout << "床的有参构造" << endl;}void display(){cout << sleep << endl;}
};//汇聚子类
//封装 沙发床类 继承于沙发 和 床
class Sofa_Bed:public Bed,public Sofa
{
private:int w;
public://Sofa_Bed(){cout << "沙发床的无参构造" << endl;}//有参构造Sofa_Bed(string sit, string s, int w,string c):Jiaju(c),Bed(s,c),Sofa(sit,c),w(w) //需要在汇聚子类中显性调用公共基类的有参构造函数{cout << "沙发床的有参构造" << endl;}
};int main()
{
// Sofa_Bed s;Sofa_Bed s1("可坐","可躺",123,"pink");return 0;
}
三、多态
类的三大属性:继承、封装和多态
函数重载是静态多态(编译时多态),函数重写是动态多态(运行时多态)
父类的指针或者引用,指向或初始化子类的对象,调用子类对父类重写的函数,进而展开子类的功能。
函数重写
1> 必须有继承关系
2> 子类和父类有同名同类型的函数
3> 父类中的该函数必须是虚函数
————虚函数指在函数前加上virtual,虚函数满足继承(父类中该函数是虚函数),继承到子类中,该函数依旧是虚函数,如果之后继续继承,继承出的函数仍是虚函数
#include <iostream>using namespace std;// 封装 周 这个类
class Zhou
{
private:string name;int age;
public://无参构造Zhou() {}//有参构造函数Zhou(string n, int a):name(n),age(a){}//virtual void speek() //表示该函数是虚函数{cout << "阿巴阿巴。。" << endl;}
};//封装 周老师 类,继承于周类
class Teacher:public Zhou
{
private:int id;public://无参构造Teacher() {}//有参构造Teacher(string n, int a, int d):Zhou(n,a),id(d){}//void speek(){cout << "看我,上多态,认真听讲" << endl;}
};//封装 游戏玩家 类 继承于Zhou类
class Player:public Zhou
{
private:string game;
public://。。Player() {}//有参构造Player(string name, int age, string g):Zhou(name,age),game(g){}//void speek(){cout << "稳住,我们能赢" << endl;}
};int main()
{Teacher t("zhangsan",34,1001);Zhou *p; //父类的指针p = &t; //父类的指针,指向子类对象 相当于承当老师这个角色p->speek(); // 上课Player g("lisi",45,"王者");p = &g; //此时是游戏玩家这个角色p->speek();return 0;
}
赋值兼容规则
只适用于公有继承。对于保护或私有继承,这些规则不适用。
- 类中有虚函数时,类里就会有一个虚指针,虚指针也满足继承
- 虚指针在类的最前面,虚指针指向了一个虚函数表,虚函数表里记录了虚函数,包括子类对父类重写的函数。
- 虚指针和虚函数表是实现多态的重要机制。
虚析构函数
虚析构函数用来解决父类指针指向子类时,父类指针释放,导致子类自拓展的空间没有得到释放
#include <iostream>using namespace std;//封装 人 类
class Person
{
private:string name;
public://Person() {}//有参构造函数Person(string n):name(n){}virtual ~Person() //虚析构函数 满足继承{cout << "Person::析构函数" << endl;}
};//封装 学生 继承于人
class Stu:public Person
{
private:int id;
public://Stu(){}//有参构造Stu(string n , int i):Person(n),id(i){}~Stu(){cout << "Stu::析构函数" << endl;}
};int main()
{Person *p = new Stu("张三",1001);delete p; //如果没有虚析构函数,进行释放p是,子类自己拓展的空间就没有释放--内存泄漏return 0;
}
四、抽象类
纯虚函数是在 C++ 中用于实现接口和抽象类的一种机制。纯虚函数在基类中声明但不定义,它没有函数体,只有一个函数原型,并且必须在派生类中被重写(覆盖)。
在基类中声明纯虚函数会自动生成一个虚函数表(vtable),该表包含指向类中所有虚函数(包括纯虚函数)的指针。每个具有虚函数的对象都有一个隐藏的成员,即虚指针(vptr),它在对象构造时自动设置,用于指向与该对象类型关联的虚函数表。
派生类必须重写所有基类中的纯虚函数,否则该派生类也会成为一个抽象类,不能被实例化。这种多态行为不仅适用于使用 new
动态创建的对象,也适用于自动(栈)或静态存储期的对象,只要你通过基类指针或引用来访问它们。
如果一个类没有任何数据成员并且只有纯虚函数,通常将其称为接口。虽然构造函数不能是虚函数或纯虚函数,但析构函数可以(并且通常应该)是虚函数,以确保正确的析构行为。
virtual 函数返回值类型 函数名(形参列表) = 0; //纯虚函数
抽象类一般是用来被继承的,它不能实例化出具体的一个对象,抽象类中至少有一个纯虚函数。
#include <iostream>
#include <string>// 基类(抽象类)
class Hero {
public:virtual void attack() = 0; // 纯虚函数virtual void defend() = 0; // 纯虚函数
};// 派生类1
class Warrior : public Hero {
public:void attack() override {std::cout << "Wqm attacks!" << std::endl;}
// void defend() override {
// std::cout << "Wqm defends!" << std::endl;
// }
};
class Warrior2 : public Warrior {
public:
// void attack() override {
// std::cout << "Wqm attacks!" << std::endl;
// }void defend() override {std::cout << "Wqm defends!" << std::endl;}
};// 派生类2
class Mage : public Hero {
public:void attack() override {std::cout << "Mage casts a spell!" << std::endl;}void defend() override {std::cout << "Mage uses a shield!" << std::endl;}
};int main() {Hero* hero1 = new Warrior2();Hero* hero2 = new Mage();hero1->attack();hero1->defend();hero2->attack();hero2->defend();delete hero1;delete hero2;hero1=nullptr;hero2=nullptr;return 0;
}
五、模板
C++另一个编程思想——泛型编程,主要利用的技术 模板
两个重要的模板机制:函数模板和类模板
“只是一个框架并不是万能的”
5.1函数模板
建立一个通用的函数,其返回值类型或者形参类型 不具体制定,用一个虚拟的类型来代替。
template <typename T>
函数的声明或定义template ----->表示开始创建模板
typename -->表明后面的符号是数据类型,typename 也可以用class代替
T ----->表示数据类型,可以其他符号代替
#include <iostream>using namespace std;//创建函数模板
template <typename T>
void fun(T &a, T &b)
{T temp;temp = a;a = b;b = temp;
}//void fun(int &a, int &b)
//{
// int temp;
// temp = a;
// a = b;
// b = temp;//}
//void fun(double &a, double &b)
//{
// double temp;
// temp = a;
// a = b;
// b = temp;
//}//void fun(char &a, char &b)
//{
// char temp;
// temp = a;
// a = b;
// b = temp;
//}int main()
{int a = 10, b = 20;fun(a,b);cout << a << " " << b << endl;double c = 1.3, d = 1.4;fun(c, d);cout << c << " " << d << endl;return 0;
}
作业
#include <iostream>
#include <string>// 基类(抽象类)
class Animal {
public:virtual void perform() = 0; // 纯虚函数virtual void size()=0;virtual ~Animal(){}
};class Lion : public Animal {
public:void perform() override{std::cout << "Jump" << std::endl;}void size() override{std::cout << "big" << std::endl;}
};class Tiger : public Animal {
public:void perform() override{std::cout << "Sit Down" << std::endl;}void size() override{std::cout << "big" << std::endl;}
};int main() {Animal* hero1 = new Lion();Animal* hero2 = new Tiger();hero1->perform();hero1->size();hero2->perform();hero2->size();delete hero1;delete hero2;hero1=nullptr;hero2=nullptr;return 0;
}