[C++]: 多态

1.多态的条件

  • 调用函数的对象必须是指针或者引用
  • 被调用函数必须是虚函数,且完成了虚函数
    那么什么是虚函数?
    虚函数:在类的成员函数前面加上virtual关键字
    虚函数的重写:派生类中有一个跟基类函数名、参数、返回值都相同的虚函数,我们就称派生类虚函数重写了基类的虚函数,也叫虚函数的覆盖。
  • 虚函数重写的例外:协变
    重写的虚函数返回值可以不同,但是必须分别是基类指针和派生类指针或者引用。
  • 虚函数的作用:使用基类指针调用任一派生类对象时,基类指针调用其虚成员函数,则会调用其真正的指向的成员函数。如果没有虚函数,则基类指针不管调用哪个派生类对象,调用时都会调用基类中定义的那个函数。

2.析构函数重写问题

  • 析构函数最好写成虚函数,避免基类指针指向派生类对象时,析构调用父类的析构函数。
    来看如下demo:
#include<iostream>
class Father {
public:
	~Father() { std::cout << "~Father()" << std::endl; }
};
class Son : public Father {
	~Son() { std::cout << "~Son()" << std::endl; }
};
int main()
{
	Father* f = new Son();
	delete f;
	return 0;
}

//程序输出结果为:~Father()
  • 基类中的析构函数如果是虚函数,那么派生类析构函数就重写了基类的析构函数。这里它们函数名看起来不一样,违背了重写原则,但实际上,编译后的析构函数名称统一处理成destructor

3.重写、重载、重定义的区别

  • 重载:两个函数在同一作用域、函数名/参数相同
  • 重写:两个函数分别在基类和派生类两个作用域、函数名/参数/返回值都必须相同
  • 重定义:两个函数分别在基类和派生类两个作用域、函数名相同、两个基类和派生类的同名函数不构成重写就是重定义。

4.多态实现的原理

4.1虚函数表

来看这样一个demo:

class Base {
public:
	virtual void f() {
		std::cout << "f()" << std::endl;
	}
private:
	int _a;
};

int main()
{
	std::cout << sizeof(Base) << std::endl;
	return 0;
}

程序输出结果为8,除了_a占用了4个字节外,还有一个指针_vfptr占了四个大小,这个指针指向了虚函数表,称之为虚函数表指针,一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中, 虚函数表也简称虚表。
我们继续看如下demo:

class Base {
public:    
	virtual void Func1() { cout << "Base::Func1()" << endl; }
	virtual void Func2() { cout << "Base::Func2()" << endl; }
	void Func3() { cout << "Base::Func3()" << endl; }
private:    
	int _b = 1;
};

class Derive : public Base 
{ 
public:    
	virtual void Func1() { cout << "Derive::Func1()" << endl; }
private:   
	int _d = 2; 
};

int main() {
	Base b;    
	Derive d;
	return 0;
}

我们可以在调试窗口看到如下信息:
在这里插入图片描述
Derive继承了Base类后,保存了其指针,除此之外,因为fun1函数重写了基类的fun1函数,所以在Base的虚表里是Base::Fun1(),而在Derive的虚表了是Derive::Fun1()
总结一下派生类的虚表生成:

  • 先将基类中的虚表内容拷贝一份到派生类虚表中
  • 如果派生类重写了基 类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
  • 派生类自己新增加的虚函数按其在 派生类中的声明次序增加到派生类虚表的最后

4.2多继承中的虚函数表

我们看如下demo

 class Base1 { 
 public:    
	virtual void func1() { cout << "Base1::func1" << endl; }    
	virtual void func2() { cout << "Base1::func2" << endl; } 
 private:   
	int b1;
 };
 class Base2 { 
 public:    
	virtual void func1() { cout << "Base2::func1" << endl; }   
	virtual void func2() { cout << "Base2::func2" << endl; } 
 private:  
	 int b2; 
 };

 class Derive : public Base1, public Base2 {
 public:    
	virtual void func1() { cout << "Derive::func1" << endl; }   
	virtual void func3() { cout << "Derive::func3" << endl; } 
 private:   
	 int d1; 
 };

 

 int main() {
	 Base1 b1;
	 Base2 b2;
	 Derive d;
	 
	 return 0;
 }

通过调试窗口我们可以发现,在多继承中派生类的未重写函数放在第一个继承基类部分的选好念书表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值