Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 构建/销毁期间的虚拟调用_C++_Constructor_Polymorphism_Language Lawyer_Virtual Functions - Fatal编程技术网

C++ 构建/销毁期间的虚拟调用

C++ 构建/销毁期间的虚拟调用,c++,constructor,polymorphism,language-lawyer,virtual-functions,C++,Constructor,Polymorphism,Language Lawyer,Virtual Functions,C++标准12.7/4规定: 当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或销毁过程中,调用应用的对象是正在构造或销毁的对象(称之为x),调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生类中重写它的函数。如果虚拟函数调用使用显式类成员访问(5.2.5),并且对象表达式引用x的完整对象或该对象的一个基类子对象,但不引用x或其一个基类子对象,则行为未定义 这个文本在我检查的所有版本中都是相同的(尽管在C++03中是第12.7/3段) 我的问题是关于短语“使

C++标准12.7/4规定:

当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或销毁过程中,调用应用的对象是正在构造或销毁的对象(称之为
x
),调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生类中重写它的函数。如果虚拟函数调用使用显式类成员访问(5.2.5),并且对象表达式引用
x
的完整对象或该对象的一个基类子对象,但不引用
x
或其一个基类子对象,则行为未定义

这个文本在我检查的所有版本中都是相同的(尽管在C++03中是第12.7/3段)

我的问题是关于短语“使用显式类成员访问权”。该短语的要点可能是指出,在构造函数/析构函数体中,使用隐式
this->
的虚拟调用是安全的,因为对象表达式确实引用对象
x

struct A;
A* p;

struct A {
    A() { p = this; }
    virtual ~A() { if (p == this) p = nullptr; }
    virtual void f() {}
};

struct B {
    B();
    virtual ~B();
    virtual void g() {}
};

struct C : public A, public B {
    virtual void f() {}
    virtual void g() {}
};

B::B() {
    if (p) p->f(); // UB if `p` and `this` point at same complete object
    g();           // Definitely safe, calls B::g().
}

B::~B() {
    if (p) p->f(); // UB if `p` and `this` point at same complete object
    g();           // Definitely safe, calls B::g().
}

int main() {
    C c;      // UB in B::B() and B::~B()!
}
但是,如果虚函数调用在构造函数或析构函数的定义中不是语法上的,而是间接调用的,该怎么办?这个程序的行为是什么

#include <iostream>

struct A {
    virtual void f() { std::cout << "A::f()\n"; }
    void h() { f(); }
};

struct B {
    explicit B(A& a) { a.h(); }
};

struct C : public A, public B {
    C() : A(), B(static_cast<A&>(*this)) {}
    virtual void f() { std::cout << "C::f()\n"; }
};

int main() {
    C c;
}
#包括
结构A{

虚空f(){std::cout它不应该有任何区别

标准上说:

直接或间接调用虚函数时

然而,您的编译器可能有一个bug-可能是因为它优化了
h
中的代码,认为它了解正在发生的事情(实际上并没有做正确的事情)。您没有提到您正在使用的编译器,因此无法确定是否有缺陷报告

编辑:几周前发布的g++4.8.2和clang++3.5(如果有区别的话,使用-std=c++11)在析构函数中调用
c::f()
,在第一个测试用例的构造函数中调用
a::f()
。在第二个测试用例中,g++调用
a::f()
,其中clang++调用
c::f()
。很明显,编译器在这里似乎“随心所欲”。[请注意,由于它是“未定义的”,它可以做各种不同的事情,包括“您期望的”]

(在第一个测试用例中,我将
p
修改为
a
,使其能够编译,并在
f
g
函数中添加了打印输出)

9.3.1/3(In)表示

当id表达式(5.1)不属于类成员访问语法(5.2.5)的一部分且不用于形成 指向成员(5.3.1)的指针用于X类成员的上下文(5.1.1), 如果名称查找(3.4)将id表达式中的名称解析为某个类的非静态非类型成员 C、 如果id表达式可能被计算,或者C是X或X的基类,则id表达式为 转换为类成员访问表达式(5.2.5),使用(*this)(9.3.2)作为 运算符的左侧


在第二个示例中,这意味着A::h()的主体被转换为(*this).f(),使调用成为显式的类成员访问。因此,12.7/4的最后一行适用;行为未定义。

抱歉,修复了第二个程序。我应该知道发布我实际尝试过的内容之一,而不是“基本相同”的内容.所谓缺陷报告,我的意思是与标准本身相反。如果这句话不适用,我不清楚实际应该发生什么。就像引用的部分所说的“直接或间接”-因此,如果您直接或间接调用函数,它应该具有相同的行为。我认为没有必要在这一点上进行更正。因此,您的结论是,即使最后一句不适用,第二个程序也有UB?这确实是我的结论。一般来说,最好避免在构造函数和析构函数中进行虚拟调用s、 因为很容易出错,并最终执行未定义的操作。那么,什么时候会有不使用显式类成员访问的虚拟函数调用?我想不出会发生这种情况,因为在这种情况下没有指定任何内容。短语“使用显式类成员访问”这显然是胡说八道;这是需要纠正的!