struct CA{
CA(){ CA::foo(); } // calling foo() without CA is wrong
virtual int foo(){}
};
struct CB {
CB(){ CB::foo(); } // calling foo() without CB is wrong
virtual int foo();
};
我看不出这两个类之间有任何明显的区别,考虑到必须在程序中定义非纯虚函数,必须在CB::foo
某个地方定义,并且它是在类定义中定义还是无关紧要别的地方。
在这两种情况下,调用foo()
(没有明确的限定)都是错误的。foo()
使用和之间的区别在于Type::foo()
,在第一种情况下,您使用的是动态调度,此时最终将调用函数的最终覆盖器,而在限定的情况下 ( ),额外的限定禁止动态调度。Type::foo
重要的是要注意此时。对象的动态类型在类型层次结构中的构造过程中发生变化。在评估基本构造函数时,动态类型是base
,因此最终覆盖器只能从该类中获取。基本构造函数完成后,动态类型将更改为层次结构中的下一个类型,并且可以从这两种类型中选择最终的覆盖器,依此类推。
struct base {
base() { foo() };
virtual void foo() { std::cout << "base\n"; }
};
struct d1 : base {
d1() : base() { foo() };
};
struct d2 : d1 {
d2() : d1() { foo() };
virtual void foo() { std::cout << "d2\n"; }
};
类型对象的d2
构造将从构造base
子对象开始,foo()
那里的调用将被分派给最终的覆盖器,此时它只考虑base
类型并打印"base"
. 然后d1
执行构造函数,调用foo()
在此级别选择最终覆盖器,它仍然是base::foo()
. 完成后,对d2
构造函数进行评估。在这一点上,最终的覆盖器正在打印d2::foo()
并"d2"
产生:
base
base
d2
请注意,即使我们正在创建的唯一完整对象是 type d2
,它也暂时表现为一个base
对象,然后作为一个d1
对象,并且只有当d2
构造函数开始执行时,它才开始表现为一个d2
对象并调用d2
. 这被许多人认为是令人困惑的。
请注意,因为每个级别的对象的动态类型正是正在评估的构造函数的类型,所以最终的覆盖器将始终被分派给该类型。添加额外的资格并没有太大的区别,除非在处理......
纯虚函数
纯虚函数可以有定义。pure 限定符意味着派生类型必须提供该虚函数的定义,也意味着在执行动态调度时永远不会调用纯虚函数(如果已定义)。在这种特殊情况下,禁用动态调度的额外限定可用于调用该特定实现:
struct base {
base() { base::foo(); } // [*]
virtual void foo() = 0;
};
void base::foo() { std::cout << "base\n"; }
struct derived : base {
derived() : base() { foo(); base::foo(); }
virtual void foo() { std::cout << "derived\n"; }
};
foo
这是对in的非限定调用格式错误的唯一情况[*]
,因为当该构造函数被评估时foo
是纯虚拟的并且没有最终覆盖器。另一方面,添加限定条件会禁用动态调度,上面的代码将编译并运行。另请注意,在derived
构造函数中,您可以使用额外的限定来选择函数的特定定义,该定义base::foo
可能不是此级别的最终覆盖器。上面的代码打印:
base
derived
base