关于C++ Primer中“透过基类调用被屏蔽的虚函数”
关于C++ Primer中“通过基类调用被屏蔽的虚函数”
在C++ Primer 15.5.4中有一个例子,看了很久没有看懂,请教一下大家(分割线中是书中的原话);
===============================================================
class Base {
public:
virtual int fcn();
};
class D1 : public Base {
public:
// hides fcn in the base; this fcn is not virtual
int fcn(int); // parameter list differs from fcn in Base
// D1 inherits definition of Base::fcn()
};
class D2 : public D1 {
public:
int fcn(int); // nonvirtual function hides D1::fcn(int)
int fcn(); // redefines virtual fcn from Base
};
从 Base 继承的虚函数不能通过 D1 对象(或 D1 的引用或指针)调用,因为该函数被 fcn(int) 的定义屏蔽了。
通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类:
Base bobj; D1 d1obj; D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); // ok: virtual call, will call Base::fcnat run time
bp2->fcn(); // ok: virtual call, will call Base::fcnat run time
bp3->fcn(); // ok: virtual call, will call D2::fcnat run time
===============================================================
对于bp1和bp3,都没有问题,但是对于bp2 ,为什么是可以调用的,存在如下疑问:
1) bp2 实际指向的是D1 类型的对象,但是D1 类 中定义了fcn,屏蔽了Base中的fcn,为什么会调用Base中的fcn而不出错?
2) 对于“通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类” 这句话,无法理解;对于虚函数,不是应该动态绑定吗?动态绑定的后果就是很有可能调用派生类中重新定义的虚函数;为什么这里讲“忽略派生类”? 如果忽略了派生类,如何实现动态绑定?
------解决方案--------------------
覆盖(也叫做多态)是指派生类重新实现或者改写了基类的成员函数,其特征是:1、不同的作用域(分别位于派生类和基类中)。2、函数名称相同搜索。3、函数的参数也完全相同。4、基类必须有virtual关键字。
注意第三点,class D1 中的int fcn(int) 并不满足多态的条件,也无法动态绑定,因此只能调用基类的函数
------解决方案--------------------
单看那一句,是错的。
但要联系上下文。
“通过基类类型的引用或指针调用(虚)函数时,(如果子类中没有重写该虚函数且定义了一个同名函数),编译器将在基类中查找该(虚)函数而忽略派生类(的同名函数)”
------解决方案--------------------
撸主啊 这个估计没人能帮助你了 悟性。
不过也不急,你还年轻,我也是反反复复纠结了很多遍之后才懂了。
同样是看这本书,effective C++ ,你不要乱黑我们的 meyers 哦,遮掩也要在子类的作用域。
简单的 你需要理解 作用域、静态类型、动态类型。
复杂的,深入的,彻底的,请看:《深度探索C++对象模型》。
------解决方案--------------------
查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。
------解决方案--------------------
关于1: 事实上 你可以用代码试一下 而不是一味的讨论 毕竟实践才是真理
在C++ Primer 15.5.4中有一个例子,看了很久没有看懂,请教一下大家(分割线中是书中的原话);
===============================================================
class Base {
public:
virtual int fcn();
};
class D1 : public Base {
public:
// hides fcn in the base; this fcn is not virtual
int fcn(int); // parameter list differs from fcn in Base
// D1 inherits definition of Base::fcn()
};
class D2 : public D1 {
public:
int fcn(int); // nonvirtual function hides D1::fcn(int)
int fcn(); // redefines virtual fcn from Base
};
从 Base 继承的虚函数不能通过 D1 对象(或 D1 的引用或指针)调用,因为该函数被 fcn(int) 的定义屏蔽了。
通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类:
Base bobj; D1 d1obj; D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); // ok: virtual call, will call Base::fcnat run time
bp2->fcn(); // ok: virtual call, will call Base::fcnat run time
bp3->fcn(); // ok: virtual call, will call D2::fcnat run time
===============================================================
对于bp1和bp3,都没有问题,但是对于bp2 ,为什么是可以调用的,存在如下疑问:
1) bp2 实际指向的是D1 类型的对象,但是D1 类 中定义了fcn,屏蔽了Base中的fcn,为什么会调用Base中的fcn而不出错?
2) 对于“通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类” 这句话,无法理解;对于虚函数,不是应该动态绑定吗?动态绑定的后果就是很有可能调用派生类中重新定义的虚函数;为什么这里讲“忽略派生类”? 如果忽略了派生类,如何实现动态绑定?
------解决方案--------------------
覆盖(也叫做多态)是指派生类重新实现或者改写了基类的成员函数,其特征是:1、不同的作用域(分别位于派生类和基类中)。2、函数名称相同搜索。3、函数的参数也完全相同。4、基类必须有virtual关键字。
注意第三点,class D1 中的int fcn(int) 并不满足多态的条件,也无法动态绑定,因此只能调用基类的函数
------解决方案--------------------
单看那一句,是错的。
但要联系上下文。
“通过基类类型的引用或指针调用(虚)函数时,(如果子类中没有重写该虚函数且定义了一个同名函数),编译器将在基类中查找该(虚)函数而忽略派生类(的同名函数)”
------解决方案--------------------
撸主啊 这个估计没人能帮助你了 悟性。
不过也不急,你还年轻,我也是反反复复纠结了很多遍之后才懂了。
同样是看这本书,effective C++ ,你不要乱黑我们的 meyers 哦,遮掩也要在子类的作用域。
简单的 你需要理解 作用域、静态类型、动态类型。
复杂的,深入的,彻底的,请看:《深度探索C++对象模型》。
------解决方案--------------------
查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。
------解决方案--------------------
前面的说错了,应该是这样
1>D1中的fcn也是虚函数,你不声明编译器帮你完成,而且是继承来的。D1中也有两个fcn函数,一个是继承来的,一个是自己定义的,也就是函数的重载,你在调用的时候没有传参,所以自动调用的是基类的fcn。
2>"通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类"这句话是没有问题的,动态绑定我不懂,我就说我对这句话的理解吧。 这句话的意思是当你在基类中定义了一个虚函数的时候,通过基类的指针或者引用调用该函数的时候会直接调用基类的这个函数,而忽略了子类。原因呢是因为所有的子类都会继承基类的虚函数,而如果在子函数中定义了同名同参的函数的时候,基类的虚函数会自动隐藏,也就是说此时使用基类类型的指针调用该函数的时候调用的就是子类中的函数。
关于1,我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数
关于2,我认为你说的和这句话矛盾,这句话说忽略派生类,但是你说的却会“调用子类(派生类)中的函数”,如果忽略了派生类,还如何调用派生类函数呢?
关于1: 事实上 你可以用代码试一下 而不是一味的讨论 毕竟实践才是真理