本专栏目的
- 更新C/C++的基础语法,包括C++的一些新特性
前言
- 通过前面几节课,我们学习了抽象、封装、继承相关的概念,接下来我们将讲解多态,多态他非常神奇,正式有了他,类才能出现多样性特征;
- C语言后面也会继续更新知识点,如内联汇编;
- 欢迎收藏 + 关注,本人将会持续更新。
文章目录
问题思考?
如果子类定义了与父类中定义相同函数会发生什么?如下面代码所示:
#include <iostream>
using namespace std;
class Parent
{
public:
void show() {
cout << "I am father" << endl;
}
};
class Son : public Parent
{
public:
void show() {
cout << "I am son" << endl;
}
};
void print(Parent& p) {
p.show();
}
int main()
{
Parent pa;
Son so;
print(pa);
print(so); // 子赋值给父亲,可以当作父亲用
return 0;
}
输出:
I am father
I am father
但是,如何在传入不同对象的时候输出相应的数据呢? 这个就是我们接下来要学的多态。
面向对象新需求
上面的这一种场景,需要C++需要做的事情是:
- print函数中,传递什么对象调用什么对象的show函数,传递父类的,就调用父类的,传递子类的,就调用子类的。
解决方案:虚函数
- 在父类中,在能让子类重写的函数的前面必须加上
virtual
关键字 - 在子类中,在重写的父类的虚函数后面加上
override
关键字,表示是虚函数重写(非必须,但是加上可以防止重写的虚函数写错)
虚函数重写概念
派生类(父类)中有一个跟基类(子类)完全相同的函数(即派生类虚函数与基类虚函数的返回值类型、函数名、参数列表完全相同)
多态的意义探究
面向对象三大概念:
封装:提取事物的属性与方法
继承:代码复用——可以用父类的代码
多态:在代码复用基础上,实现不同功能
案例:打印矩形和圆形坐标和面积
矩形:x,y,length,width
圆形:x,y,radius
在这个案例中,我们可以划分:
- 封装:将矩形和圆形共有属性抽象出来,这里是x,y坐标,同时将共有方法抽象出来,这里是打印坐标和面积,将这些封装成一个基类A;
- 继承:分别定义矩形、圆形类,继承基类A,同时定义属于自己的属性或者方法,这里是矩形中定义属性length,width,圆形定义radius;
- 多态:基类中定义了方法(打印坐标和面积),在圆形和矩形中分别重写这两个方法;
- 测试:利用子类可以赋值给父类的特征,实现传入什么类就输出什么类对应的API。
代码实现如下:
#include <iostream>
using namespace std;
class Geometry
{
public:
Geometry(int x, int y) : m_x(x), m_y(y) {}
virtual void print_coordinates() {}
virtual void print_area() {}
int m_x; // 测试:整形
int m_y;
};
class Rectangle : public Geometry
{
public:
Rectangle(int x, int y, int width, int length)
: Geometry(x, y),
m_width(width),
m_length(length) {
}
// 重写
void print_coordinates() override
{
std::cout << "x: " << m_x << " y: " << m_y << std::endl;
}
void print_area() override
{
std::cout << "Rectangle area: " << m_width * m_length << std::endl;
}
int m_width;
int m_length;
};
class Round : public Geometry
{
public:
Round(int x, int y, int riduas)
: Geometry(x, y),
m_riduas(riduas)
{
}
// 重写
void print_coordinates() override
{
std::cout << "x: " << m_x << " y: " << m_y << std::endl;
}
void print_area() override
{
std::cout << "Round area: " << 3.14 * m_riduas * m_riduas << std::endl;
}
int m_riduas;
};
void test(Geometry& various)
{
various.print_coordinates();
various.print_area();
}
int main()
{
Rectangle rect(1, 2, 3, 4);
Round round(5, 6, 1);
test(rect);
test(round);
return 0;
}
输出:
x: 1 y: 2
Rectangle area: 12
x: 5 y: 6
Round area: 3.14
多态成立的三要素:(结合解决方案)
- 要有继承:多态发生在父子类之间
- 要有虚函数重写:重写了虚函数,才能进行动态绑定
- (解决方案)
- 要有父类指针(引用)指向子类对象,传递参数的时候必须为引用或者指针,推荐常引用
虚析构
前置知识:
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
析构函数可以是虚的。通过父类指针释放所有的子类资源
虚析构:
虚析构:通过父类去释放子类的时候,如果分类没有虚析构是不会调用子类的析构函数的,会调用子类的析构函数,想要通过父类去释放子类, 必须在父类定义虚析构。
让我们来看一下,这段代码结果会是什么:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << __FUNCSIG__ << endl;
}
~Base()
{
cout << __FUNCSIG__ << endl;
}
};
class Derive : public Base
{
private:
char* _str;
public:
Derive()
{
_str = new char[10] { "wy" };
cout << __FUNCSIG__ << endl;
}
~Derive()
{
delete _str;
cout << __FUNCSIG__ << endl;
}
};
int main()
{
Base* base = new Derive;
delete base;
return 0;
}
结果:
__cdecl Base::Base(void)
__cdecl Derive::Derive(void)
__cdecl Base::~Base(void)
但是这个时候,子类的内存没有释放(_str),这样就造成了内存泄露问题
标签:函数,--,子类,void,多态,C++,int,父类,指针 From: https://blog.csdn.net/weixin_74085818/article/details/142883623