C++系列-多态的基本语法

weixin_48668114 2024-08-28 12:04:00 阅读 75

多态的基本语法

多态的含义静态多态动态多态

多态的底层原理多态中的final和overridefinaloverride:

多态的应用和优点计算器简单实现电脑组装的实现


《游山西村》

南宋·陆游

莫笑农家腊酒浑,丰年留客足鸡豚。

山重水复疑无路,柳暗花明又一村。

箫鼓追随春社近,衣冠简朴古风存。

从今若许闲乘月,柱杖无时夜叩门。


多态的含义

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

C++多态性(Polymorphism)是面向对象编程(OOP)的一个重要特性之一。它允许我们使用统一的接口来处理不同类型的对象。多态性使得程序更加灵活、可扩展并且易于维护。


静态多态

函数重载,运算符重载这些都属于静态多态,复用函数名。编译阶段就确定了函数的地址。


动态多态

使用了一种动态绑定的技术,这个技术的核心就是虚函数表(virtual table)。运行阶段才确定函数的地址。

看一下下面这段代码,执行eat函数,想通过父类的引用指向子类的对象,用于实现子类的函数调用,可是事与愿违。

查看父类的大小,因为非静态成员函数并非属于类的对象上,所以size是1。

<code>code:

#include<iostream>

using namespace std;

class Vegetable

{ -- -->

public:

void eat()

{

cout << "多吃蔬菜对身体好哦!" << endl;

}

};

class Spinach :public Vegetable

{

public:

void eat()

{

cout << "多吃 菠菜 对身体好哦!" << endl;

}

};

class Taro :public Vegetable

{

public:

void eat()

{

cout << "多吃 芋头 对身体好哦!" << endl;

}

};

void eat(Vegetable &veg)

{

veg.eat();

}

int main()

{

Spinach sp1;

eat(sp1);

Taro ta1;

eat(ta1);

cout << "sizeof(Vegetable) = " << sizeof(Vegetable) << endl;

cout << "sizeof(Spinach) = " << sizeof(Spinach) << endl;

cout << "sizeof(Taro) = " << sizeof(Taro) << endl;

system("pause");

return 0;

}

result:

多吃蔬菜对身体好哦!

多吃蔬菜对身体好哦!

sizeof(Vegetable) = 1

sizeof(Spinach) = 1

sizeof(Taro) = 1


接着改变一个地方,在父类函数前加上virtual关键字,同时对子类中的函数进行重写。

通过父类的引用指向子类的对象,实现了子类的函数调用。

查看类的结构,大小变为4,vfptr virtual function pointer, 它指向一个vftable,和操作系统有关,有的是8。

所谓的虚函数,就是被virtual修饰的类成员函数。子类中有一个跟父类完全相同的虚函数(即子虚函数与父类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了父类的虚函数。子类在重写父类的虚函数时,在函数前可以加virtual,可以不加,建议加。

code:

#include<iostream>

using namespace std;

class Vegetable

{

public:

virtual void eat()

{

cout << "多吃蔬菜对身体好哦!" << endl;

}

};

class Spinach :public Vegetable

{

public:

virtual void eat()

{

cout << "多吃 菠菜 对身体好哦!" << endl;

}

};

class Taro :public Vegetable

{

public:

virtual void eat()

{

cout << "多吃 芋头 对身体好哦!" << endl;

}

};

void eat(Vegetable &veg)// 父类的引用指向子类的对象

{

veg.eat();

}

int main()

{

Spinach sp1;

eat(sp1);

Taro ta1;

eat(ta1);

cout << "sizeof(Vegetable) = " << sizeof(Vegetable) << endl;

cout << "sizeof(Spinach) = " << sizeof(Spinach) << endl;

cout << "sizeof(Taro) = " << sizeof(Taro) << endl;

system("pause");

return 0;

}

result:

多吃 菠菜 对身体好哦!

多吃 芋头 对身体好哦!

sizeof(Vegetable) = 4

sizeof(Spinach) = 4

sizeof(Taro) = 4

在这里插入图片描述

在这里插入图片描述


如果子类中不重写父类中的虚函数,类的结构如下:

vftable中的函数地址父类和子类是相同的,执行的都是父类的函数。

在这里插入图片描述

在这里插入图片描述


多态的底层原理

在类的结构中,有一个vfptr指向一个vftable。vftable中放置了虚函数的地址,当子类中未重写该函数时,该地址是父类函数的地址,否则为子类函数的地址。如果有多个虚函数,vftable放置的是虚函数的地址对应的数组。


多态中的final和override

有些情况下由于疏忽,比如函数名书写错误,导致无法实现多态,这种错误在编译期间是不会报出的,但在运行期间得不到正确的结果。C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

final

修饰虚函数,表示该虚函数不能再被重写。virtual 返回类型 函数名(形参) final {}

override:

检查子虚函数是否重写了父类某个虚函数,如果没有重写编译报错。virtual 返回类型 函数名(形参) override {}


多态的应用和优点

代码组织结构清晰可读性强利于代码维护

计算器简单实现

<code>//在上述代码中,我们首先定义了一个抽象基类 Operation ,其中包含一个纯虚函数 calculate 。

//然后,分别创建了加法、减法、乘法和除法的子类来实现具体的计算逻辑。

//在 main 函数中,根据用户的选择创建相应的子类对象,让父类指针指向它,并通过多态调用计算函数得到结果。

#include <iostream>

using namespace std;

// 抽象父类,纯虚函数

class Operation

{ -- -->

public:

virtual double calculate(double num1, double num2) = 0; // 纯虚函数

};

// 加法操作类

class Add : public Operation

{

public:

double calculate(double num1, double num2) override // override表示是重写父类中的函数

{

return num1 + num2;

}

};

// 减法操作类

class Subtract : public Operation

{

public:

double calculate(double num1, double num2) override

{

return num1 - num2;

}

};

// 乘法操作类

class Multiply : public Operation

{

public:

double calculate(double num1, double num2) override

{

return num1 * num2;

}

};

// 除法操作类

class Divide : public Operation {

public:

double calculate(double num1, double num2) override

{

if (num2 != 0) {

return num1 / num2;

}

else {

cout << "除数不能为 0" << endl;

return 0;

}

}

};

int main()

{

Operation* op; // 父类指针

double num1, num2, result;

int choice;

while (1)

{

cout << "------------ 请选择操作 ------------" << endl;

cout << "1. 加法" << endl;

cout << "2. 减法" << endl;

cout << "3. 乘法" << endl;

cout << "4. 除法" << endl;

cout << "0. 退出" << endl;

cin >> choice; //

if (choice == 0)

{

cout << "退出计算过程" << endl;

break;

}

else

{

cout << "请输入第一个数:";

cin >> num1;

cout << "请输入第二个数:";

cin >> num2;

switch (choice)

{

case 1:

op = new Add(); // 父类指针指向子类对象

break;

case 2:

op = new Subtract();

break;

case 3:

op = new Multiply();

break;

case 4:

op = new Divide();

break;

default:

cout << "无效的选择" << endl;

return 1;

}

result = op->calculate(num1, num2); // 通过父类指针指向的是哪一个子类对象实现其对应的计算

cout << "结果是:" << result << endl;

delete op;

}

}

system("pause");

return 0;

}


电脑组装的实现

假设电脑的主要部件为CPU,内存条,显卡, 各有不同的厂家,不同厂家的器件特性不一样,后续可能会不断增加。将每个零件封装出抽象父类,然后不同的厂家的零件定义子类,这样可以便于后续的维护和增加。定义一个计算机类,其中实现初始化,组装操作等。

在组装过程中,打印出各零件的厂家及零件类型信息。

#include <iostream>

using namespace std;

// 假设电脑的主要部件为CPU,内存条,显卡, 各有不同的厂家,不同厂家的器件特性不一样,后续可能会不断增加。

// 将每个零件封装出抽象父类,然后不同的厂家的零件定义子类,这样可以便于后续的维护和增加。

// 定义一个计算机类,其中实现初始化,组装操作等。

// 每个零件定义父类,实现多态应用,定义虚函数或者纯虚函数。

class Cpu

{

public:

virtual void calculate() { };

};

class Memory

{

public:

virtual void storage() { };

};

class VideoCard

{

public:

virtual void display() { };

};

// 不同厂家的零件继承零件父类,并实现具体的函数,实现多态应用。

class AmdCpu: public Cpu

{

public:

void calculate()

{

cout << "amd cpu calculate." << endl;

};

};

class IntelCpu : public Cpu

{

public:

void calculate()

{

cout << "intel cpu calculate." << endl;

};

};

class SamsungMemory : public Memory

{

public:

void storage()

{

cout << "samsung memory save data." << endl;

};

};

class MicronMemory : public Memory

{

public:

void storage()

{

cout << "micron memory save data." << endl;

};

};

class AmdVideoCard : public VideoCard

{

public:

void display()

{

cout << "amd video card display." << endl;

};

};

class NvidiaVideoCard : public VideoCard

{

public:

void display()

{

cout << "nvidia video card display." << endl;

};

};

// 定义计算机类,实现初始化,组装操作等,因为子类对象开辟在堆区,所以重写析构函数,实现堆区内存释放

class Computer

{

public:

Computer(Cpu *cpu, Memory *memory, VideoCard *video_card)

{

m_cpu = cpu;

m_memory = memory;

m_video_card = video_card;

}

void assemble()

{

m_cpu->calculate();

m_memory->storage();

m_video_card->display();

}

// 释放堆区内存

~Computer()

{

if (m_cpu != NULL)

{

delete m_cpu;

m_cpu = NULL;

}

if (m_memory != NULL)

{

delete m_memory;

m_memory = NULL;

}

if (m_video_card != NULL)

{

delete m_video_card;

m_video_card = NULL;

}

}

private:

Cpu *m_cpu;

Memory *m_memory;

VideoCard *m_video_card;

};

void test01()

{

Cpu *intel_cpu = new IntelCpu;

Memory *samsung_memory = new SamsungMemory;

VideoCard* amd_video_card = new AmdVideoCard;

Computer *com1 = new Computer(intel_cpu, samsung_memory, amd_video_card);

com1->assemble();

delete com1;

}

void test02()

{

Cpu* amd_cpu = new AmdCpu;

Memory* micron_memory = new MicronMemory;

VideoCard* amd_video_card = new AmdVideoCard;

Computer* com1 = new Computer(amd_cpu, micron_memory, amd_video_card);

com1->assemble();

delete com1;

}

int main()

{

cout << "\n-------- 第 1 台电脑组装 --------" << endl;

test01();

cout << "\n-------- 第 2 台电脑组装 --------" << endl;

test02();

system("pause");

return 0;

}

result:

-------- 第 1 台电脑组装 --------

intel cpu calculate.

samsung memory save data.

amd video card display.

-------- 第 2 台电脑组装 --------

amd cpu calculate.

micron memory save data.

amd video card display.

注意:部门内容来自黑马视频课程



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。