解密C++多态机制:发挥对象的多样性,实现更加智能的程序设计

在这里插入图片描述

一.多态

1.多态的用处

众所周知C++语言的三大特性:封装、多态、继承。其中多态就是去完成某个行为,但是会根据不同的对象产生不同的状态,所以叫多态。

2.多态的实现

在继承中实现多态还需要两个条件:

  1. 使用基类的指针或者引用来调用虚函数
  2. 必须在派生类对虚函数进行重写
    代码演示如下:
class A
{
public:
	virtual void func()
	{
		cout << "A::func()" << endl;
	}
};

class B : public A
{
public:
	void func()
	{
		cout << "B::func()" << endl;
	}
};


int main()
{
	B b;
	A& a = b;
	a.func();
	return 0;
}

在这里插入图片描述

3.虚函数

用virtual修饰的类成员函数叫做虚函数,虽然与虚拟继承用的是同一关键字但是两者并无关联。

	virtual void func(){cout << "A::func()" << endl;}

派生类的重写:
重写需要在派生类中编写与基类相同的虚函数:返回类型相同(协变除外)、函数名相同、参数相同(缺省值可不同),然后函数具体实现的不同,来完成多态。

协变
协变允许基类派生类虚函数的返回值不同,但是要求返回值是互为父/子类的指针或者引用。如下所示:

class A
{
public:
	A(int a = 1)
		:_a(a) {}

	virtual A* func(int val = 0)
	{
		cout << val<< endl;
		return new A;
	}

private:
	int _a;
};

class B : public A
{
public:
	B(int b = 1)
		:_b(b) {}

	virtual B* func(int val = 1)
	{
		cout << val<< endl;
		return new B;
	}
private:
	int _b;
};


int main()
{
	B b;
	A& a = b;
	a.func();
	return 0;
}

在这里插入图片描述

上图中,如果返回的不是本身自己父子类的指针/引用,则可以交换顺序!
需要注意的是在派生类重写基类的虚函数可以不加virtual

析构函数的重写
如果基类重写的析构函数,在派生类可以不加virtual也可以完成重写(因为编译器将析构函数的名称统一处理成destructor。)

4.override 和 final

这两个关键字都是C++11的新语法,final修饰虚函数 使得虚函数不可以重写
override 可以在派生类函数检查是否重写的基类虚函数,如果没有就 报错
如果不想类被继承可以采用:将构造函数设为私有、将类用final修饰为最终类

5.重载重写与重定义

重载:两个函数在同一作用域、函数名/参数相同
重写:两个函数分别在基类和派生类的作用域、函数名/参数/返回值都必须相同(协变例外)、两个函数必须是虚函数
隐藏(重定义)两个函数分别在基类和派生类的作用域、函数名相同、两个基类和派生类的同名函数不构成重写就是重定义

6.虚函数表

如果在类中定义了虚函数,那么类中就会有个虚表用来存放虚表指针。
虚函数表本质是一个存虚函数指针的指针数组
虚表中存放着虚函数,同类型对象会共用一块虚表。
子类自己的虚函数只会放到第一个父类的虚表后面,其他父类的虚表不需要存储,因为存储了也不能调用
有虚函数的类前4/8字节存储的是虚表的地址。
去虚表找 看是什么对象 (多态时)
虚函数重写只重写函数的实现 缺省值会使用父类的

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
C++中的多态性是通过虚函数和虚表来实现的。虚表是一个指向虚函数地址的指针数组,每个类都有自己的虚表。当我们定义一个类时,如果其中有虚函数,编译器就会自动生成一个指向该类虚表的指针 vptr。 假设我们有一个基类 Animal 和两个派生类 Cat 和 Dog,其中 Animal 中有一个虚函数 eat(),Cat 和 Dog 分别实现了 eat() 方法。现在我们创建了一个 Animal 类型的指针 p,指向一个 Cat 对象。 在这种情况下,p->eat() 调用的是 Cat 类中的 eat() 方法。但是,由于 p 是一个 Animal 类型的指针,编译器会根据 p 所指向的对象类型来确定调用哪个函数。因此,虚表的作用就在于解决这个问题。 下面是一个模拟实现多态对象大小的代码示例: ```c++ #include <iostream> using namespace std; class Animal { public: virtual void eat() { cout << "Animal is eating..." << endl; } }; class Cat : public Animal { public: void eat() { cout << "Cat is eating..." << endl; } }; class Dog : public Animal { public: void eat() { cout << "Dog is eating..." << endl; } }; int main() { Animal *p; p = new Cat(); cout << "Size of Animal: " << sizeof(Animal) << endl; cout << "Size of Cat: " << sizeof(Cat) << endl; cout << "Size of Dog: " << sizeof(Dog) << endl; cout << "Size of Animal pointer: " << sizeof(p) << endl; p->eat(); delete p; return 0; } ``` 输出结果如下: ``` Size of Animal: 8 Size of Cat: 8 Size of Dog: 8 Size of Animal pointer: 8 Cat is eating... ``` 可以看到,Animal、Cat 和 Dog 的大小都是 8,因为它们都有一个指向虚表的指针 vptr,而 Animal 类型的指针 p 的大小也是 8。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aomnitrix

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值