关于dynamic binding及static binding的思考

飞天御剑流 2012-09-12 11:53:26
先看个例子:


class A
{
public:

virtural void fun( void ){ cout << "A"; return; }

};

class B : public A
{
public:

virtual void fun( void ){ cout << "B"; return; }

};

int main( void )
{
A* p = new B;
p -> A::fun( );
return 0;
}

A::fun( )这个叫虚拟函数的静态解析。这是近日在回答一个问题时的类似代码,原来偶对这个问题是比较确定的,但后来细细斟酌,却也未必。问题是:虚拟函数的静态解析属于dynamic binding还是static binding??

有些人可能毫不迟疑地认为应该是static binding,因为对于static binding,一般的定义是编译期确定;但是,对照C++标准,却也未必,C++标准是这样说的:

[Note: the interpretation of the call of a virtual function depends on the type of the object for which it is
called (the dynamic type), whereas the interpretation of a call of a nonvirtual member function depends
only on the type of the pointer or reference denoting that object (the static type) (5.2.2). ]

C++标准明确说了虚拟函数调用是依赖动态类型的,这里并没有加上对qualified-id的例外。那么,按照这个条款,A::fun()也是根据动态类型解析的,解析的目标是找到final overrider,方法是member name lookup:

10.3 Virtual functions
.............Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations.

这个member name lookup是怎样的呢?C++标准是这样描述的:

10.2 Member name lookup

.........For an id-expression, name lookup begins in the class scope of this; for a qualified-id, name lookup begins in the scope of the nested-name-specifier.

上述fun就是一个id-expression,这个条款明确地规定对于一个qualified-id(这个例子中的A::fun),名称搜索从嵌套名称指示符(nested-name-specifier)开始。

也就是说,qualified-id的名称搜索总体上依然属于虚拟函数dynamic binding的搜索方法范畴,但是方法中的一个分支。

于是,这个语义就跟static binding的语义有冲突了,A::fun()是属于dynamic binding呢,还是static binding?这个问题耐人寻味,如果说是C++03标准尚未考虑到的情况,但C++11并没有进行修改。BS、lippmen也没有对这个问题有明确的说明。

欢迎大家踊跃发表看法!
...全文
602 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
mujiok2003 2012-09-21
  • 打赏
  • 举报
回复

class B {
public :
virtual void f ()
{
}
};

class D : public B
{
public :
void f (){}
};

int main()
{
D d;
B* p = &d;
p->f();
p->B::f();
return 0;
}


运行时反汇编:

p->f();
008B356C mov eax,dword ptr [p]
008B356F mov edx,dword ptr [eax]
008B3571 mov esi,esp
008B3573 mov ecx,dword ptr [p]
008B3576 mov eax,dword ptr [edx]
008B3578 call eax
008B357A cmp esi,esp
008B357C call @ILT+3440(__RTC_CheckEsp) (8B1D75h)
p->B::f();
008B3581 mov ecx,dword ptr [p]
008B3584 call B::f (8B1587h) //直接调用,没有查vtabl
mujiok2003 2012-09-21
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
A::fun( )这个叫虚拟函数的静态解析。这是近日在回答一个问题时的类似代码,原来偶对这个问题是比较确定的,但后来细细斟酌,却也未必。问题是:虚拟函数的静态解析属于dynamic binding还是static binding??

有些人可能毫不迟疑地认为应该是static binding,因为对于static binding,一般的定义是编译期确定;但是,对照C++标准,却也未必,C++标准是这样说的:
[/Quote]

我好像有回复那个帖子.
其一:

10.3.1
Virtual functions support dynamic binding and object-oriented programming. A class that declares or inherits a virtual
function is called a polymorphic class.

并没有说不支持static bind.

其二:
10.7.13
Explicit qualification with the scope operator (5.1)
suppresses
the virtual call mechanism. [ Example:
class B { public : virtual void f (); };
class D : public B { public : void f (); };
void D::f () { / . . . / B::f ();//等价于this->Base::f() }

Here, the function call in D::f really does call B::f and not D::f. —end example ]

当然这里也并没说是static binding. 我看到标准并没有提到static binding是什么.

其三:
我看了几个编译器,obj.Base::f() 这种确实有查找vtable的操作

欢迎继续讨论。

www_adintr_com 2012-09-21
  • 打赏
  • 举报
回复
当然应该是静态绑定的.
即使标准里面有些许文字和这个语义冲突,也不应该去咬文嚼字,要从整体上领略其设计原则。标准也是人订的,是人都可能犯错,谁能保证那么大的一个文案里面每一个脚注都面面俱到?

对于虚函数调用(the call of a virtual function)来说,加 A:: 这样的限定词是没有任何意义的,虚函数用的是运行时的对象的动态信息。而像 A:: 这样的类型信息是编译时的,在运行时已经不存在了。 如果把 p->A::fun( ); 按动态绑定来处理,那里面的 A:: 没有任何意义, C++ 就应该禁止这种语法。

而另一方面,即使是虚函数,也有直接调用它的需求. C++ 要满足这种需求,必须提供某种语法来支持,显然 p->A::fun() 正是为此而设计的。
mujiok2003 2012-09-21
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 的回复:]
Explicit qualification with the scope operator (5.1) suppresses the virtual call mechanism.
这条款放在10.3的最后了,没看见,有这条款可以认为虚函数的静态解析属于静态绑定。只是10.3clause6说得太绝对了,应该加上例外情况。
[/Quote]

原来你找到了!!
liutengfeigo 2012-09-20
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

对于这样的代码,我一贯绕着走,基本不想去搞清楚。
搞清楚了也没用的东西。
[/Quote]
学习了
飞天御剑流 2012-09-20
  • 打赏
  • 举报
回复
Explicit qualification with the scope operator (5.1) suppresses the virtual call mechanism.
这条款放在10.3的最后了,没看见,有这条款可以认为虚函数的静态解析属于静态绑定。只是10.3clause6说得太绝对了,应该加上例外情况。
taodm 2012-09-18
  • 打赏
  • 举报
回复
对于这样的代码,我一贯绕着走,基本不想去搞清楚。
搞清楚了也没用的东西。
taodm 2012-09-18
  • 打赏
  • 举报
回复
我貌似记得mayers反复教导我们,不要轻言自己或者编译器错了。
gcc的作者们,可能哪个都比我们更认真研究过标准的原文。
ri_aje 2012-09-18
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

那个,y是VC的一贯输出。所以,说是gcc的bug轻率了点,说和标准没关系,则极可能太托大自己了。

引用 19 楼 的回复:
引用 17 楼 的回复:

因为当年gcc的3个版本给出了x->y->x式的答案变化。
引用 14 楼 的回复:
很诧异 BS 为什么没有正面回答,因为 10.3/15 说的明明白白,莫非发问的时候这句话还不在标准里面?那得多少年以前的事儿了,我看了一下……
[/Quote]
看不明白了,用 VS2010 试了一下,输出也是 A 呀,不是和 g++ 一样吗?
另外你也别误会,我没有看到你说的那三个版本 gcc,因此只是从 x->y-> 的模式猜其中 y 是 bug,所以我说“看起来像”,怎么让你说的我妄自尊大并找借口为标准开托似的。
taodm 2012-09-18
  • 打赏
  • 举报
回复
那个,y是VC的一贯输出。所以,说是gcc的bug轻率了点,说和标准没关系,则极可能太托大自己了。

[Quote=引用 19 楼 的回复:]
引用 17 楼 的回复:

因为当年gcc的3个版本给出了x->y->x式的答案变化。
引用 14 楼 的回复:
很诧异 BS 为什么没有正面回答,因为 10.3/15 说的明明白白,莫非发问的时候这句话还不在标准里面?那得多少年以前的事儿了,我看了一下 03 年的标准都有这句啊。

看起来 y 像是 gcc 的 bug 了,不过这和标准没有关系。
[/Quote]
冻结 2012-09-18
  • 打赏
  • 举报
回复
表示不知道。。。
ri_aje 2012-09-18
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 的回复:]

因为当年gcc的3个版本给出了x->y->x式的答案变化。
引用 14 楼 的回复:
很诧异 BS 为什么没有正面回答,因为 10.3/15 说的明明白白,莫非发问的时候这句话还不在标准里面?那得多少年以前的事儿了,我看了一下 03 年的标准都有这句啊。
[/Quote]
看起来 y 像是 gcc 的 bug 了,不过这和标准没有关系。
pathuang68 2012-09-17
  • 打赏
  • 举报
回复
我觉得是static binding
franzhong 2012-09-17
  • 打赏
  • 举报
回复
学习了~
taodm 2012-09-17
  • 打赏
  • 举报
回复
因为当年gcc的3个版本给出了x->y->x式的答案变化。
[Quote=引用 14 楼 的回复:]
很诧异 BS 为什么没有正面回答,因为 10.3/15 说的明明白白,莫非发问的时候这句话还不在标准里面?那得多少年以前的事儿了,我看了一下 03 年的标准都有这句啊。
[/Quote]
ri_aje 2012-09-17
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

好吧,数年前,csdn曾经有过这么一个代码的讨论,楼主来判断判断吧。
struct A
{
virtural void fun( void ){ cout << "A"; return; }
};

struct B : public A
{
using A::fun;
};

struct C : public B
{
virtual voi……
[/Quote]
我认为行为很清楚,应该调用 A::fun(),B::fun() 的写法要求 fun 在 scope of B 里面 lookup,这样的话,只能找到 A::fun(),过程中不存在 virtual mechanism。实际上那句 using 根本没有用,因为 B : public A,A::fun 已经可见了。

很诧异 BS 为什么没有正面回答,因为 10.3/15 说的明明白白,莫非发问的时候这句话还不在标准里面?那得多少年以前的事儿了,我看了一下 03 年的标准都有这句啊。
ri_aje 2012-09-17
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

引用 7 楼 的回复:
额 LZ是怎么理解dynamic binding和static binding的呢?


偶觉得问题还不在这吧,纠结的在于

the interpretation of the call of a virtual function depends on the type of the object for which it is called (the dy……
[/Quote]
我觉得10.3/15
Explicit qualification with the scope operator (5.1) suppresses the virtual call mechanism.
明确说了,qualified-id 调用写的那个调的就是那个。所以,p->A::fun() 应该属于 static binding。

我在 #1 说了,不过为毛我的 #1 没了,最近发贴经常出现类似的情况,大家有没有类似的丢帖经历,还是我机器的问题?
hotpos 2012-09-16
  • 打赏
  • 举报
回复
static binding

p -> A::fun()的语义是 以p做对象指针 调用 A::fun函数.

明确指明了调用 类A的fun 函数, 无论fun是不是虚函数.
所以这个地方的搜索范围就限定了 A::fun,
如果类A没有fun,则搜寻其基类.

不可能根据虚函数去动态调用B::fun,
因为B::fun 不是类A的函数.
Gloveing 2012-09-16
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]
好吧,数年前,csdn曾经有过这么一个代码的讨论,楼主来判断判断吧。
struct A
{
virtural void fun( void ){ cout << "A"; return; }
};

struct B : public A
{
using A::fun;
};

struct C : public B
{
virtual void fun( v……
[/Quote]
这。。。。
表示完全不懂
taodm 2012-09-16
  • 打赏
  • 举报
回复
好吧,数年前,csdn曾经有过这么一个代码的讨论,楼主来判断判断吧。
struct A
{
virtural void fun( void ){ cout << "A"; return; }
};

struct B : public A
{
using A::fun;
};

struct C : public B
{
virtual void fun( void ){ cout << "B"; return; }
};

int main( void )
{
C * p = new C;
p -> B::fun( );
return 0;
}

大致就是这样的了,虚函数、using 加 限定
有人发mail给了C++之父,他没有直接正面回答。

加载更多回复(9)

64,646

社区成员

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

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