关于C++ Primer中“通过基类调用被屏蔽的虚函数”

listenxu 2014-07-07 09:57:34
在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) 对于“通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类” 这句话,无法理解;对于虚函数,不是应该动态绑定吗?动态绑定的后果就是很有可能调用派生类中重新定义的虚函数;为什么这里讲“忽略派生类”? 如果忽略了派生类,如何实现动态绑定?
...全文
472 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
苏客达 2014-07-07
  • 打赏
  • 举报
回复
引用 14 楼 listenxu 的回复:
[quote=引用 11 楼 sukiida 的回复:] [quote=引用 6 楼 listenxu 的回复:] [quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。[/quote] 如果把“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法”当成一个C++的定律的话,确实可以解释bp2->fcn() 可以成功的原因; 那么,关于effective C++中的遮掩,可能需要加一个限制条件,就是遮掩只发生在使用派生类的对象来调用成员函数的情况下,而不适合于用基类指针或引用(虽然指向派生类的对象)来调用成员函数的情况; [/quote] 查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 11 楼 sukiida 的回复:
[quote=引用 6 楼 listenxu 的回复:] [quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。[/quote] 如果把“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法”当成一个C++的定律的话,确实可以解释bp2->fcn() 可以成功的原因; 那么,关于effective C++中的遮掩,可能需要加一个限制条件,就是遮掩只发生在使用派生类的对象来调用成员函数的情况下,而不适合于用基类指针或引用(虽然指向派生类的对象)来调用成员函数的情况;
苏客达 2014-07-07
  • 打赏
  • 举报
回复
不好意思,更正下, 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来通过指向派生类的基类静态类型指针 bp2 动态调用 bp2->fcn(); 则是合法。
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 9 楼 victor1960 的回复:
你需要弄清楚 虚函数的实现原理,没有你想象的那么神奇
effective C++中,关于遮掩有这样一句话,“此例内含→组混合了public 和private 名称,以及一组成员变量和成员函数名称。 这些成员函数包括pure virtual, impure virtual 和non-virtual 三种,这是为了强调我们 谈的是名称,和其他无关。” 这个说明遮掩和是否虚函数无关啊
苏客达 2014-07-07
  • 打赏
  • 举报
回复
引用 6 楼 listenxu 的回复:
[quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 8 楼 victor1960 的回复:
撸主啊 这个估计没人能帮助你了 悟性。 不过也不急,你还年轻,我也是反反复复纠结了很多遍之后才懂了。 同样是看这本书,effective C++ ,你不要乱黑我们的 meyers 哦,遮掩也要在子类的作用域。 简单的 你需要理解 作用域、静态类型、动态类型。 复杂的,深入的,彻底的,请看:《深度探索C++对象模型》。
怎么理解“遮掩也要在子类的作用域”? 是说只适合于使用子类来调用函数吗? 但是,这里bp2所指向的对象确实是子类D1,为什不适用于遮掩的概念呢? 这段真是让人纠结的茶饭不思啊;
victor1960 2014-07-07
  • 打赏
  • 举报
回复
你需要弄清楚 虚函数的实现原理,没有你想象的那么神奇
victor1960 2014-07-07
  • 打赏
  • 举报
回复
撸主啊 这个估计没人能帮助你了 悟性。 不过也不急,你还年轻,我也是反反复复纠结了很多遍之后才懂了。 同样是看这本书,effective C++ ,你不要乱黑我们的 meyers 哦,遮掩也要在子类的作用域。 简单的 你需要理解 作用域、静态类型、动态类型。 复杂的,深入的,彻底的,请看:《深度探索C++对象模型》。
sdghchj 2014-07-07
  • 打赏
  • 举报
回复
单看那一句,是错的。 但要联系上下文。 “通过基类类型的引用或指针调用(虚)函数时,(如果子类中没有重写该虚函数且定义了一个同名函数),编译器将在基类中查找该(虚)函数而忽略派生类(的同名函数)”
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 1 楼 sukiida 的回复:
1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 4 楼 u013470052 的回复:
前面的说错了,应该是这样 1>D1中的fcn也是虚函数,你不声明编译器帮你完成,而且是继承来的。D1中也有两个fcn函数,一个是继承来的,一个是自己定义的,也就是函数的重载,你在调用的时候没有传参,所以自动调用的是基类的fcn。 2>"通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类"这句话是没有问题的,动态绑定我不懂,我就说我对这句话的理解吧。 这句话的意思是当你在基类中定义了一个虚函数的时候,通过基类的指针或者引用调用该函数的时候会直接调用基类的这个函数,而忽略了子类。原因呢是因为所有的子类都会继承基类的虚函数,而如果在子函数中定义了同名同参的函数的时候,基类的虚函数会自动隐藏,也就是说此时使用基类类型的指针调用该函数的时候调用的就是子类中的函数。
关于1,我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 关于2,我认为你说的和这句话矛盾,这句话说忽略派生类,但是你说的却会“调用子类(派生类)中的函数”,如果忽略了派生类,还如何调用派生类函数呢?
初見的畫面 2014-07-07
  • 打赏
  • 举报
回复
前面的说错了,应该是这样 1>D1中的fcn也是虚函数,你不声明编译器帮你完成,而且是继承来的。D1中也有两个fcn函数,一个是继承来的,一个是自己定义的,也就是函数的重载,你在调用的时候没有传参,所以自动调用的是基类的fcn。 2>"通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类"这句话是没有问题的,动态绑定我不懂,我就说我对这句话的理解吧。 这句话的意思是当你在基类中定义了一个虚函数的时候,通过基类的指针或者引用调用该函数的时候会直接调用基类的这个函数,而忽略了子类。原因呢是因为所有的子类都会继承基类的虚函数,而如果在子函数中定义了同名同参的函数的时候,基类的虚函数会自动隐藏,也就是说此时使用基类类型的指针调用该函数的时候调用的就是子类中的函数。
初見的畫面 2014-07-07
  • 打赏
  • 举报
回复
首先你得明白虚函数的定义以及作用。 被声明为虚函数的函数在被子类继承时,无论子类中继承来的函数是否声明为virtual,编译器都自动认为它是虚函数。 而如果在子类中声明或者定义了同名函数(但是参数不同),那么基类的虚函数将会隐藏。使用基类类型的指针指向子类对象时是无法访问的。 1>所以说D1中的fcn也是虚函数,你不声明编译器帮你完成,而且是继承来的。D1中也有两个fcn函数,一个是继承来的,一个是自己定义的,也就是函数的重载,你在调用的时候没有传参,所以自动调用的是基类的fcn。 2>"通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类"这句话是没有问题的,动态绑定我不懂,我就说我对这句话的理解吧。 这句话的意思是当你在基类中定义了一个虚函数的时候,通过基类的指针或者引用调用该函数的时候会直接调用基类的这个函数,而忽略了子类。原因呢是因为所有的子类都会继承基类的虚函数,而如果在子函数中定义了同名同参的函数的时候,基类的虚函数会自动隐藏,也就是说此时使用基类类型的指针调用该函数的时候调用的就是子类中的函数。
熠de 2014-07-07
  • 打赏
  • 举报
回复
覆盖(也叫做多态)是指派生类重新实现或者改写了基类的成员函数,其特征是:1、不同的作用域(分别位于派生类和基类中)。2、函数名称相同搜索。3、函数的参数也完全相同。4、基类必须有virtual关键字。 注意第三点,class D1 中的int fcn(int) 并不满足多态的条件,也无法动态绑定,因此只能调用基类的函数
苏客达 2014-07-07
  • 打赏
  • 举报
回复
1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
初見的畫面 2014-07-07
  • 打赏
  • 举报
回复
引用 5 楼 listenxu 的回复:
[quote=引用 4 楼 u013470052 的回复:] 前面的说错了,应该是这样 1>D1中的fcn也是虚函数,你不声明编译器帮你完成,而且是继承来的。D1中也有两个fcn函数,一个是继承来的,一个是自己定义的,也就是函数的重载,你在调用的时候没有传参,所以自动调用的是基类的fcn。 2>"通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类"这句话是没有问题的,动态绑定我不懂,我就说我对这句话的理解吧。 这句话的意思是当你在基类中定义了一个虚函数的时候,通过基类的指针或者引用调用该函数的时候会直接调用基类的这个函数,而忽略了子类。原因呢是因为所有的子类都会继承基类的虚函数,而如果在子函数中定义了同名同参的函数的时候,基类的虚函数会自动隐藏,也就是说此时使用基类类型的指针调用该函数的时候调用的就是子类中的函数。
关于1,我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 关于2,我认为你说的和这句话矛盾,这句话说忽略派生类,但是你说的却会“调用子类(派生类)中的函数”,如果忽略了派生类,还如何调用派生类函数呢?[/quote] 关于1: 事实上 你可以用代码试一下 而不是一味的讨论 毕竟实践才是真理 关于2:我说的是另一种情况,是与前一句话相对立的情况。前面是对前一句话的解释。
listenxu 2014-07-07
  • 打赏
  • 举报
回复
引用 15 楼 sukiida 的回复:
[quote=引用 14 楼 listenxu 的回复:] [quote=引用 11 楼 sukiida 的回复:] [quote=引用 6 楼 listenxu 的回复:] [quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。[/quote] 如果把“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法”当成一个C++的定律的话,确实可以解释bp2->fcn() 可以成功的原因; 那么,关于effective C++中的遮掩,可能需要加一个限制条件,就是遮掩只发生在使用派生类的对象来调用成员函数的情况下,而不适合于用基类指针或引用(虽然指向派生类的对象)来调用成员函数的情况; [/quote] 查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。[/quote]
引用 15 楼 sukiida 的回复:
[quote=引用 14 楼 listenxu 的回复:] [quote=引用 11 楼 sukiida 的回复:] [quote=引用 6 楼 listenxu 的回复:] [quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。[/quote] 如果把“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法”当成一个C++的定律的话,确实可以解释bp2->fcn() 可以成功的原因; 那么,关于effective C++中的遮掩,可能需要加一个限制条件,就是遮掩只发生在使用派生类的对象来调用成员函数的情况下,而不适合于用基类指针或引用(虽然指向派生类的对象)来调用成员函数的情况; [/quote] 查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。[/quote]
引用 15 楼 sukiida 的回复:
[quote=引用 14 楼 listenxu 的回复:] [quote=引用 11 楼 sukiida 的回复:] [quote=引用 6 楼 listenxu 的回复:] [quote=引用 1 楼 sukiida 的回复:] 1) D2中存在两个fcn,分别是基类继承来的,和自己定义的。因为参数列表不同,这是重载的两个不同函数。 ->我之前理解和你一样,但是在effective C++ 条款33中,明确讲了这种是属于遮掩,也就是如果D1中定义了和Base中同名的函数,调用函数的时候,不管是否是虚函数,形参是否相同,D1中会屏蔽Base中的同名函数,而无法调用到基类的函数 2)看上下文,肯定有具体情况,一句摘出来的话不能判断书里具体是要表达什么意思。
--> 书中就孤零零的这一句[/quote] 首先,在例子的前面一页有一个深色强调句子:如果派生类定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。也就是D1 d1; d1.fcn(); 这样的调用是违法的。 然后,给的例子是为了说明“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法。 最后,“关键概念:名字查找与继承”说明什么这样的调用是合法的。前两条简单,直接略过。第三条,在基类找到了满足bp2->fcn(); 的原型,调用合法。第四条,普通函数的话,就调用基类的成员函数。虚函数则通过虚函数表查找。“如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行那个函数版本”。我的理解是通过动态调用到D1中的 fcn() 但是此时 fcn() 作用域仍然在基类中(因为是通过基类指针调用的),没有被屏蔽,调用合法。 之前我也没有注意到这个细节,有什么错误欢迎后面的高手指正。[/quote] 如果把“通过基类调用被屏蔽的虚函数”。虽然 d1.fcn(); 违法,但是通过指向基类的指针来动态调用 bp2->fcn(); 则是合法”当成一个C++的定律的话,确实可以解释bp2->fcn() 可以成功的原因; 那么,关于effective C++中的遮掩,可能需要加一个限制条件,就是遮掩只发生在使用派生类的对象来调用成员函数的情况下,而不适合于用基类指针或引用(虽然指向派生类的对象)来调用成员函数的情况; [/quote] 查了下,虚函数调用编译时被处理成 pObj->_vptr->vtable[]。也就是对编译器来说,bp2->fcn() 调用的仍然是Base::fcn()而不是D1::fcn();所以屏蔽作用在这个时候是不存在的。程序运行期间会根据bp2绑定的实际类去vtable[]里面查实际应该调用的函数。[/quote] 对,按照你这样解释就明白多了; 遮掩是发生在编译期,而在这个例子中,编译器只知道静态类型,即基类类型(因为是基类类型的指针),所以对编译器而言,他认为bp2->fcn() 调用的仍然是Base::fcn();到了运行期,就按照虚函数表的法则来进行调用,所以对于没有定义虚函数的重载版本的派生类而言,指向他的基类的指针在运行期就会使用基类自己定义的版本; 如果是直接用D1的对象来调用fcn(),如d1obj.fcn(),那么编译器知道的静态类型为D1,这时候,编译器认为需要调用D1::fcn(),因为D1中定义了fcn(int),遮掩了Base::fcn(), 所以,对于这个调用,会发生编译错误; 总结起来,是否发生遮掩,是根据编译器所知道的静态类型决定的,如果静态类型是基类,则派生类无法遮掩基类中定义的函数;如果静态类型是派生类(对象,指针,或者引用都一样),而派生类定义了新的同名函数,则会发生遮掩;

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧