C++多态及原理

文章目录


多态的概念

 多态的概念:通俗来说,就是多种形态,具体点就是去完成某种行为,不同的对象去完成时会长生不同的状态。

 比如:买车票就是一种多态行为,不同的人去买票价格可能不一样,就像成人票和学生票的价格就会有差异 ,但是买票是同一种行为,不同的人买就会产生不同的状态或者结果,这就叫做多态!

一、构成多态的条件

1、重写

1.1、虚函数:即被virtual修饰的类成员函数称为虚函数

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票—> 全价" << endl;
	}
};

1.2、虚函数的重写(覆盖)

    派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名、参数列表完全相同),

称子类的虚函数重写了基类的虚函数。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票—> 全价" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生票—>打八折" << endl;
	}
};

2、必须是父类的指针或者引用调用虚函数

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票—> 全价" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生票—>打八折" << endl;
	}
};
void Func(Person& p)//父类的引用
{
	p.BuyTicket();
}
int main()
{
	Person adult;
	Student collegestudent;
	Func(adult);
	Func(collegestudent);
	return 0;
}

二、重写

1.构成重写两个的条件

1.1、必须是虚函数

1.2、派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名、参数列表完全相同)

1.3、析构函数的重写(基类与派生类的函数名字不相同)

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 virtual 关键字,
都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,在Windows环境下编译器对析构函数的名称做了特殊处理
编译后析构函数的名称统一处理成 destructor
class Person {
public:
	virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:
	virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函
//数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	
	delete p1;
	delete p2;
	return 0;
}

2.重写的两个特例

2.1、子类函数不加virtual也依旧构成重写

原因是继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性,但是在实际应用中虚函数最好加virtual修饰,避免混淆。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票—>全价" << endl;
	}
};
class Student :public Person
{
public:
	//1、子类虚函数不加virtual,依旧构成重写(但是在实际中通常都加virtual)
	void BuyTicket()
	{
		cout << "学生票—>半价" << endl;
	}
};

2.2、重写协变   

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};

三、多态原理

1.虚函数表

   一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数
的地址要被放到虚函数表中,虚函数表也简称虚表。
结论:1、虚函数表本质是一个存虚函数指针的指针数组,在VS环境下这个数组最后面放了一个 nullptr
           2、 派生类的虚表生成:
                  a.先将基类中的虚表内容拷贝一份到派生类虚表中
                  b. 如果派生 类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 
                  c. 派生类自己 新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
注意:
虚表存的是虚函数指针,不是虚函数 ,虚函数和普通函数一样的,都是存在代码段的,只是
他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针

2.多态调用

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
	return 0;
}

1. 观察下图的红色箭头我们看到, p 是指向 mike 对象时, p->BuyTicket mike 的虚表中找到虚
函数是 Person::BuyTicket
2. 观察下图的蓝色箭头我们看到, p 是指向 johnson 对象时, p->BuyTicket johson 的虚表中
找到虚函数是 Student::BuyTicket
3. 这样就实现出了不同对象去完成同一行为时,展现出不同的形态。
4. 反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是对象的指针或引用调
用虚函数。

 

总结

多态的特性(构成多态的条件)

1.必须构成重写

  重载、覆盖(重写)、隐藏(重定义)的对比

 

2.必须是父类的指针或者引用去调用虚函数

多态的本质原理就是符合两个多态的条件,调用时会到对象的虚表中找到对应的虚函数地址,进行调用,如果不是父类的指针或者引用去调用虚函数,那么此时就是普通函数的调用就不会产生多态的行为或者状态

注意:

多态调用是指程序运行时去指向的对象的虚表中找到函数地址,而普通函数的调用是在编译链接时就确定了函数的地址,运行时直接调用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值