c++多态

最近查看有关多态的知识,感觉有些陌生,因此梳理一下c++多态的知识

     多态:分为静多态和动多态,分别对应编译时期绑定的称为静多态(早绑定),非编译时期绑定的称为动多态(晚绑定)

静多态通过函数模板和重载实现.本质是接口的复用。

函数重载:

int add(int a, int b)
{
	return a + b;
}
double add(double a, double b)
{
	return a + b;
}
函数模板( 泛型编程):
template<typename T>
T add(T a, T b)
{
	return a + b;
}
多态:为了实现代码的接口重用,无论传递过来那个对象,函数都能找到合适的实现方法。

class Base {
public:
	void print()
	{
		cout << "Base::print()" << endl;
	}
};

class Derived : public Base {
public:
	//void print()
	//{
	//  cout << "Derived::print()" << endl;
	//}

};

void Test()
{
	Base b;
	Derived d;
	b.print();//调用基类的打印Base::print()
	d.print();//子类继承了基类的print()且子类本身没有print(),因此此处还是调用基类的print()打印 Base::print()
}

int main()
{
	Test();
	return 0;
}
我们取消派生类print()函数注释,

    Base b;
    Derived d;
    b.print();//调用基类的print()打印Base::print()
    d.print();//子类本身中print(),此时构成了重定义,即基类中的print()被隐藏,因此调用的是子类的print()打印    Derived::print()
    d.Base::print();//若想调用基类的print()需要加类的作用域限定符,打印Base::print()
试试用指针来调用:

    Base b;
    Derived d;
    Base* pb = &b;
    Derived* pd = &d;
    pb->print();//pb指向基类,打印Base::print()//call Base::fun(0DE14C9H)

    pd->print();//pd指向子类,打印Derived::print()

    pb = &d; pb->print();//pb指向子类,却打印Base::print()
引用也相同。

为什么基类对象引用派生类对象,打印的是基类函数?

因为在静多态时期,编译时期就将函数的实现和函数的地址绑定,无论指针还是引用都在编译时期就确定了基类对象,因此打印基类函数。为了避免这种情况,我们引入了动态多态

什么叫多态?

基类指针(同引用)指向不同的派生类对象,调用派生类和基类的同名覆盖方法,基类指针指向哪个派生类对象,调用的就是它的覆盖方法!程序运行期间(非编译期)才能判断所引用对象的实际类型,根据其实际类型调用相应的方法。多态就是通过动态绑定来实现的  =》  动态绑定通过vfptrvftable来实现的,继承+虚函数实现的。

实现格式:

基类成员函数加virtual,声明为虚函数,派生类重写该函数,编译器将实现动态绑定。

class Base {
public:
	virtual void print()
	{
		cout << "print::fun()" << endl;
	}
};
派生类保持不变
void Test()
{
	Derived d;

	Base* pb = &d;
	pb->print();//基类指针pb指向子类,可以打印Derived::print()//
	Base& rb = d;
	rb.print();//基类对象pb引用子类,可以打印Derived::print()//lea eax,[d]   mov dword ptr[rb],eax
}
实现动态绑定条件:虚函数,基类指针/引用调用虚函数。

重载:1、在同一作用域中
    2、函数名相同、参数个数或参数类型不同,返回值可同可不同
隐藏(重定义):1、在不同作用域中,分别在基类和派生类中
    2、隐藏只要求函数名相同就行
    3、在派生类中只要不是重写就是隐藏
 隐藏是因为在子类中定义了与基类同名的函数,而将基类的函数隐藏掉,要想访问基类的函数,则必须加作用域限定符。

覆盖(重写):
    1、不再同一作用域中,分别在父类和子类中
    2、要求函数名相同,函数参数列表相同,返回值也相同
    3、基类函数必须是虚函数(virtual)
    4、访问修饰符可以不同

纯虚函数

在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象 。纯虚函数必须在派生类中重新定义以后,派生类才能实例化出对象。

class Person
{
	virtual void Display() = 0; 
protected:
	string _name;
};
class Student : public Person
{
	void Display()
	{
		cout << "Student" << endl;
	}
};
int main()
{
	//Person p;//不能实例化对象
	Student stu;
}

抽象类 含有纯虚函数的类就成为抽象类。抽象类只是一种基本的数据类型,用户需要在这个基础上根据自己的需要定义处各种功能的派生类。抽象类的作用就是为一个类族提供一个公共接口。抽象类不能定义对象,一个水果类可以派生出橘子香蕉苹果等等,但是水果类本身定义对象并不合理也没有必要。但是可以定义指向抽象类的指针变量,通过这个指针变量可以实现多态。

1 只有类的非静态成员函数才能定义为虚函数,静态成员函数和友元函数不能定义为虚函数。 

只有类的非静态成员函数才能定义为虚函数,静态成员函数和友元函数不能定义为虚函数。 

3 最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构 
函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)。 








  












  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值