在C ++中,我们不应该在构造函数/析构函数中调用虚函数。
如果我用类范围显式调用这些函数怎么办?
基本上我们说我们不想从虚拟中获利。
下面的伪代码是否正确?
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();
}
答案 0 :(得分:1)
调用CB::foo()
不是动态调度的,所以等待您或任何阅读此代码的人都不会有任何不幸的惊喜。这很好。事实上,简单地调用foo()
也没关系,因为在这种情况下标准相同,尽管你可能想为你的同事留下评论。
答案 1 :(得分:1)
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()
之间的区别在于,在第一种情况下,您正在使用动态分派,此时最终将调用函数的最终覆盖 < / em>,而在合格的情况下(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"
1}}输入并打印d1
。然后执行foo()
构造函数,调用base::foo()
在此级别选择最终覆盖,仍为d2
。完成后,将评估d2::foo()
构造函数。此时最终的覆盖是"d2"
并打印base
base
d2
,产生:
d2
请注意,即使他认为我只创建了我们正在创建的对象类型为base
的对象,它也会暂时表现为d1
对象,然后表现为d2
对象,并且仅在{ {1}}构造函数开始执行它开始表现为d2
对象并调用d2
。这被许多人认为是令人困惑的。
请注意,因为每个级别的对象的 dynamic 类型正是要计算的构造函数的类型,所以最终的覆盖将始终被分派到该类型。除了处理......之外,添加额外的资格并没有多大区别。
纯虚拟功能
纯虚函数可以有一个定义。纯限定符意味着派生类型必须提供该虚函数的定义,并且还意味着在执行动态调度时永远不会调用纯虚函数(如果已定义)。在该特定情况下,禁用动态分派的额外限定可用于调用该特定实现:
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
中对[*]
的无限制调用生成错误的唯一情况,因为正在评估该构造函数时foo
是纯虚拟的,并且没有 final超控器。另一方面,添加限定禁用动态调度,上面的代码将编译并运行。另请注意,在derived
构造函数中,您可以使用额外的限定条件来选择函数base::foo
的特定定义,该定义可能不是此级别的最终覆盖者。上面的代码打印:
base
derived
base