022 看上去像多态——真正理解多态了吗?

c++ 专栏收录该内容
11 篇文章 0 订阅

这道题太好了,原来我以为就是一道脑洞题,后来发现实际上这道题在考察我们是否能分清多态和非多态语句。我就没分清,又重新搞明白了

#include <iostream>
using namespace std;
class B { 
	private: 
		int nBVal; 
	public: 
		/*virtual*/void Print() //这两处是否有virtual结果将截然不同
		{ cout << "nBVal="<< nBVal << endl; } 
		/*virtual*/void Fun() 
		{cout << "B::Fun" << endl; } //这道题没有多态
		B ( int n ) { nBVal = n;} 
};
// 在此处补充你的代码
class D: public B
{
    private:
        int nDVal;
    public:
        D(int m):B(3*m),nDVal(m){};//必须指明基类构造函数的初始化
        void Fun()
        {
            cout<<"D::Fun"<<endl;
        }
        void Print()
        {
            B::Print();
            cout<<"nDVal="<<nDVal<<endl;
        }


};

int main() { 
	B * pb; D * pd; //因此还需要有个类D 应该是派生类
	D d(4); d.Fun(); //输出D fun 第一个
	pb = new B(2); pd = new D(8); 
	pb -> Fun(); pd->Fun(); //输出B D的两个fun
	pb->Print (); pd->Print (); //B 2 24 D 8 这里坑,应该是pd的print
	pb = & d; pb->Fun(); //B fun!!!此处并非多态!
	pb->Print(); //B 12!!!此处也并非多态!这两行语句是这道题想要告诉我们的
    system("pause");
	return 0;
}

我来回顾梳理一下:
首先:公有派生的赋值兼容规则:本该出现基类(对象、引用、指针)的地方,也可以出现派生类(对象、引用、指针)
那么,举个典型的例子:
有一个基类和一个派生类,他们都有同名同参成员函数。
我们用基类指针指向该派生类
之后通过该指针调用这个同名同参的成员函数。
好了问题来了,调用的是谁的成员函数?基类的还是派生类的?
这就是多态与否的区别,也是静态联编和动态联编 的区别。
如果,该成员函数就是普通的:
那么编译器此处采用静态联编,在编译过程即会按照基类指针的方法去“理解”被指向的派生类的地址。注意派生类的结构本身是基类在前,因而此时将先查找基类中成员函数并调用。注意,此时若调用某个派生类独有的成员函数,将出错。因为编译器并不认为它指向了一个派生类对象。只读取地址前面基类对象的内容。
如果,该成员函数在基类中声明是虚函数:
则编译采用动态联编,编译过程编译器不去考虑该指针指向的是谁的函数,直接跳过。(所以很虚)
而在运行过程中,编译器也不会管这到底是基类还是派生类对象,它将直接访问该指针的前4/8个字节所对应的虚函数表,在虚函数表中存有各虚函数地址。从而找到所对应的虚函数。因此,此时编译器查表查的是派生类对象的虚函数表,自然调用的是派生类对象的成员函数。这就是多态的原理。
引用应该是类似的,多态同样会访问虚函数表。
注意:并非不是非多态情况下,派生类的成员函数就调用不了,那是需要用一个派生类的指针直接指向派生类对象。(引用类似)
这题出的真的太好了。
除此之外也复习了一下,派生类对象的构造函数该怎么写:基类没有无参构造函数,因而初始化列表必须也有基类构造函数。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值