nit*_*ian 5 c++ constructor runtime-error linker-errors pure-virtual
请考虑以下示例代码:
#include <iostream>
using namespace std;
class base
{
public:
base()
{
bar(); //Line1
this->bar(); //Line2
base *bptr = this;
bptr->bar(); //Line3
((base*)(this))->bar(); //Line4
}
virtual void bar() = 0;
};
class derived: base
{
public:
void bar()
{
cout << "vfunc in derived class\n";
}
};
int main()
{
derived d;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码bar()在基类中具有纯虚函数,在派生类中被覆盖.纯虚函数bar()在基类中没有定义.
现在专注于Line1,Line2,Line3和Line4.
我理解:Line1给出编译错误,因为无法从ctor调用纯虚函数.
问题:
为什么Line2并Line4没有给出compilation error在同样的理由I understand上面的语句?呼叫进入Line2并Line4最终linker-error只会导致.
为什么Line3既不提供编译错误也不提供链接器错误但run-time exception只给出?
纯虚函数通过构造函数调用时UB的实时示例:

在所有四种情况下,行为都是不确定的; 所以到底发生了什么取决于你的编译器在无效输入面前做了什么.
编译器可能会尝试诊断问题以发出警告; 对于第1行来说这很容易做到,而对于其他行来说则更难,这可以解释为什么你只看到第1行的警告.
当从构造函数调用虚函数时,编译器知道应该调用哪个重载,因此它可能生成静态调用.这就是您从第2行和第4行收到链接错误的原因.
在第3行中,编译器必须已经确定它是否太难以确定是否可以生成静态调用,因此它生成了动态调用.跟踪变量的值比确定临时指针必须引用的更难this,并且通常根本不可能.这就是你在那里遇到运行时错误的原因.
当然,所有这些都是未定义的行为,并且可能会从编译器变为编译器,或者根据月亮的相位而变化.
如果函数有一个实现,那么这将是有效称之为静态,如Base::bar(),或bptr->Base::bar().动态调用它仍然会给出未定义的行为.
从构造函数调用Pure虚函数是未定义的行为,编译器可以自由地显示任何行为.
参考:
C++ 03标准10.4/6:
"可以从抽象类的构造函数(或析构函数)调用成员函数;对于从这样的构造函数创建(或销毁)的对象,直接或间接地对纯虚函数进行虚拟调用(10.3)的效果(或析构函数)未定义."
C++标准在以下位置定义了未定义的行为:
[defns.undefined] 1.3.12未定义的行为
行为,例如在使用错误的程序结构或错误数据时可能出现的行为,本国际标准没有规定任何要求.当本国际标准忽略对行为的任何明确定义的描述时,也可能预期未定义的行为.[ 注意:允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征的文件表示(有或没有发出诊断消息),终止翻译或执行(发布诊断信息).许多错误的程序结构不会产生未定义的行为; 他们需要被诊断出来.]
| 归档时间: |
|
| 查看次数: |
1492 次 |
| 最近记录: |