C++ Day6

目录

一、菱形继承

[1.1 概念](#1.1 概念)

[1.2 格式](#1.2 格式)

二、虚继承

[2.1 作用](#2.1 作用)

[2.2 格式](#2.2 格式)

2.3注意

三、多态

3.1函数重写

[3.2 虚函数](#3.2 虚函数)

[3.3 赋值兼容规则](#3.3 赋值兼容规则)

[3.4 多态中,函数重写的原理](#3.4 多态中,函数重写的原理)

[3.5 虚析构函数](#3.5 虚析构函数)

[3.5.1 格式](#3.5.1 格式)

[3.6 纯虚函数](#3.6 纯虚函数)

3.6.1格式

四、抽象类

五、模板

5.1模板的特点

[5.2 函数模板](#5.2 函数模板)

5.2.1作用

[5.2.2 格式](#5.2.2 格式)

六、练习

[1.定义一个基类 Animal,其中有一个虚函数 perform(),用于在子类中实现不同的表演行为。](#1.定义一个基类 Animal,其中有一个虚函数 perform(),用于在子类中实现不同的表演行为。)

结果为:

2.用函数模板实现不同数据类型的交换功能。

结果为:


一、菱形继承

1.1 概念

菱形继承又称为钻石继承,由公共基类派生出多个中间子类,又由多个中间子类共同派生出汇聚子类。汇聚子类会得到,中间子类从公共基类继承下来的多份成员。

问题:

汇聚子类会得到,中间子类从公共基类继承下来的多份成员,造成空间浪费,没有必要,还会对公共基类的成员多次初始化,或释放。

1.2 格式

cpp 复制代码
                 A       --------公共基类
               /    \
              B      C    ------- 中间子类
               \    /
                  D       --------汇聚子类

解决:虚继承

cpp 复制代码
#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;
}

二、虚继承

2.1 作用

可以让汇聚子类只保留一份 中间子类从公共基类继承下来的成员。

2.2 格式

在中间子类的继承方式前 加上 virtual

cpp 复制代码
class 类名 : virtual 继承方式 类名   //中间子类
{
    中间子类的拓展;
};

2.3注意

1> 中间子类虚继承公共基类后,汇聚子类的初始化列表,先调用中间子类的有参构造函数,中间子类再调用公共基类的有参构造函数,但是呢,虚继承之后,只保留一份中间子类从公共基类继承下来的有参构造函数,意味着不知道调用哪一个中间子类继承下来的公共基类的构造函数,这样就会默认调用公共基类的无参构造函数。

2> 如果汇聚子类想要对公共基类的数据成员初始化,需要显性调用公共基类的构造函数。

cpp 复制代码
#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;
}

三、多态

类的三大属性:封装、继承、多态

静态多态(函数重载)、动态多态(运行时)

一种形式多种状态

多态就像一个人,可以有很多角色或者行为,取决于不同情境

父类的指针或者引用,指向或初始化子类的对象,调用子类对父类重写的函数,进而展开子类的功能。

3.1函数重写

1> 必须有继承关系

2> 子类和父类有同名同类型的函数

3> 父类中的该函数必须是虚函数

3.2 虚函数

1> 在函数前加上 virtual ---->该函数是虚函数

2> 虚函数满足继承,也就是说父类中该函数是虚函数,继承到子类中,该函数依旧是虚函数,如果子类再被继承,"孙类"中该函数还是虚函数....

cpp 复制代码
#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;
}

3.3 赋值兼容规则

父类的指针或者引用,指向或初始化子类的对象

3.4 多态中,函数重写的原理

  • 类中有虚函数时,类里就会有一个虚指针,虚指针也满足继承
  • 虚指针在类的最前面,虚指针指向了一个虚函数表,虚函数表里记录了虚函数,包括子类对父类重写的函数。
  • 虚指针和虚函数表是实现多态的重要机制。

3.5 虚析构函数

虚析构函数用来解决 父类指针指向子类时,父类指针释放,导致子类自拓展的空间没有得到释放

3.5.1 格式

cpp 复制代码
virtual 析构函数
{}
cpp 复制代码
#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;
}

3.6 纯虚函数

当父类中虚函数被子类用来重写,且没有定义的意义,这个时候,一般把父类中的虚函数设置成纯虚函数。

3.6.1格式

cpp 复制代码
virtual 函数返回值类型 函数名(形参列表) = 0; //纯虚函数

四、抽象类

抽象类一般是用来被继承的,它不能实例化出具体的一个对象,抽象类中至少有一个纯虚函数。

如果子类没有对父类的纯虚函数重写,那么子类也是抽象类,不能实例化一个对象

cpp 复制代码
#include <iostream>

using namespace std;

//..
class A  //抽象类
{
private:
    int a;
public:
    A() {}
    virtual void show() = 0;  //纯虚函数    
};


class B:public A
{
public:
    B() {}    
    void show()  //如果子类没有对父类的纯虚函数重写,那么子类也是抽象类,不能实例化一个对象
    {}
};


int main()
{
    B b;
    return 0;
}

五、模板

模板就是一个通用的模具。大大提高代码的复用性。

C++还有另一个编程思想 ,泛型编程,主要利用的技术 模板

C++中有两个重要的模板机制:函数模板和类模板

5.1模板的特点

1> 模板不能直接使用,它只是一个框架

2> 模板不是万能的

5.2 函数模板

5.2.1作用

建立一个通用的函数,其返回值类型或者形参类型 不具体制定,用一个虚拟的类型来代替。

5.2.2 格式

cpp 复制代码
template <typename T>
函数的声明或定义

解释:

template ----->表示开始创建模板

typename -->表明后面的符号是数据类型,typename 也可以用class代替

T ----->表示数据类型,可以其他符号代替

cpp 复制代码
#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;
}

六、练习

1.定义一个基类 Animal,其中有一个虚函数 perform(),用于在子类中实现不同的表演行为。

以下是一个简单的比喻,将多态概念与生活中的实际情况相联系:

比喻:动物园的讲解员和动物表演

想象一下你去了一家动物园,看到了许多不同种类的动物,如狮子、、猴子等。现在,动物园里有一位讲解员,他会为每种动物表演做简单的介绍。

在这个场景中,我们可以将动物比作是不同的类,而每种动物表演则是类中的函数。而讲解员则是一个基类,他可以根据每种动物的特点和表演,进行相应的介绍。

cpp 复制代码
#include <iostream>

using namespace std;

class Animal{
protected:
    string species;
public:
    Animal(){}

    Animal(string sp): species(sp)
    {}

    virtual void perform(){}
};

class lion:public Animal{
public:
    lion(){}

    lion(string species):Animal(species)
    {

    }

    void perform(){
        cout << Animal::species << "跳火圈" << endl;
    }
};

class elephant:public Animal{
public:
    elephant(){}

    elephant(string sp):Animal(sp)
    {

    }

    void perform(){
        cout << Animal::species << "踩背" << endl;
    }
};

class monkey:public Animal{
public:
    monkey(){}

    monkey(string sp):Animal(sp)
    {

    }

    void perform(){
        cout << Animal::species << "偷桃" << endl;
    }
};

int main()
{

    lion it1("辛巴");
    Animal *p;
    p = &it1;
    p->perform();

    elephant it2("非洲象");
    p = &it2;
    p->perform();

    monkey it3("峨眉山猴子");
    p = &it3;
    p->perform();

    return 0;
}

结果为:

2.用函数模板实现不同数据类型的交换功能。

cpp 复制代码
#include <iostream>

using namespace std;
template <typename T>

void fun(T *a, T *b)
{
    T temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int a = 10;
    int b = 50;
    char c = 'C';
    char d = 'D';
    fun(&a,&b);
    fun(&c,&d);
    cout << "a = " << a << " b = " << b << endl;
    cout << "c = " << c << " d = " << d << endl;
    return 0;
}

结果为:

相关推荐
day3ZY16 分钟前
清理C盘缓存,电脑缓存清理怎么一键删除,操作简单的教程
c语言·开发语言·缓存
学地理的小胖砸28 分钟前
【高分系列卫星简介】
开发语言·数码相机·算法·遥感·地理信息
码农豆豆30 分钟前
4.C++中程序中的命名空间
开发语言·c++
Joker1008531 分钟前
C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现
c++
怀九日33 分钟前
C++(学习)2024.9.19
开发语言·c++·学习·重构·对象·
KookeeyLena81 小时前
如何限制任何爬虫爬取网站的图片
开发语言·c++·爬虫
m_Molly1 小时前
vs2022配置opencv==4.9.0(C++)
c++·opencv
yanyanwenmeng1 小时前
matlab基础
开发语言·算法·matlab
Yz98761 小时前
Hadoop里面MapReduce的序列化与Java序列化比较
java·大数据·jvm·hadoop·分布式·mapreduce·big data
charon87781 小时前
Unreal Engine 5 C++: 编辑器工具编写入门(中文解释)
c++·ue5·编辑器·游戏引擎·虚幻