在对象生存期内显式构造函数和虚函数调用

时间:2014-08-28 04:58:26

标签: c++ constructor

我们可以使用qualified-name调用构造函数,尽管构造函数没有名称。确实是3.4.3.2/2:

  

在查找中,不忽略函数名称和   nested-name-specifier指定一个C类:

     

- 如果在C中查找时,在嵌套名称说明符之后指定的名称是   C的注入类名(第9条)或

     

[...]

     

该名称被认为是为C类的构造函数命名。

考虑以下示例:

#include <iostream>

using std::cout;
using std::endl;

struct A
{
    virtual void foo()
    {
        cout << "A" << endl;
    }

    A(){ }
};

struct B : A
{
    virtual void foo()
    {
        cout << "B" << endl;
    }

    B()
    {
        foo();
    }  
};

struct C : B
{
    virtual void foo()
    {
        cout << "C" << endl;
    }

    C() : B(){ }      
};

C c;

int main()
{
    c.foo();
    C::C(); // Prints B
}

demo

C::C()行打印B.但目前还不清楚。第12.7 / 4节说:

  

直接或间接从a调用虚函数时   构造函数或析构函数,包括在构造期间或   破坏类的非静态数据成员,对象   调用适用是正在构建的对象(称之为x)   销毁,被调用的函数是最终的覆盖者   构造函数或析构函数的类,而不是覆盖它的一个   更多派生的课程

在显式构造函数中,调用c已经完全构造。所以我引用的规则不能用来解释这种行为。它是UB吗?你能解释一下吗?

2 个答案:

答案 0 :(得分:1)

  

我们可以使用qualified-name调用构造函数,尽管构造函数没有名称。

你的前提是完全错误的。在您引用的非常相同的段落中(§3.4.3.1[class.qual] / p2):

  

这样的构造函数名称只能在a的 declarator-id 中使用   命名构造函数或 using-declaration 的声明。

C::C();是一个命名构造函数的声明吗?不是。它是使用声明吗?显然不是。

这是不正确的。由于某种原因,Clang似乎考虑将其命名为类型 - 可能是一个错误(它对注入类名的处理在其他方面也有错误。)


我也不知道你是怎么得出这样的结论:C::C();的行为 - 顺便提一下,如果你考虑C::C来命名这个类型,那么它可能会受到影响c的状态,在表达式和任何相关函数中都没有出现。

对对象的假设显式构造函数调用必须看起来像c.C::C();,因为构造函数是非静态成员函数。允许你在已经构造的对象上调用构造函数是没有任何意义的 - 这甚至意味着什么?

答案 1 :(得分:0)

线......

C::C();

正在输出......

B

因为foo()是从B的构造函数中调用的,因此它将使用B的{​​{1}}版本。

任何时候调用foo()的构造函数, C调用C::C()的构造函数,该构造函数调用A的构造函数,其中B被调用,因此使用了foo()的{​​{1}}版本,然后B的构造函数体(你留空)终于被调用了。


让我们忽略从foo()调用C这样的事实不符合C ++标准的事实。您似乎误解了第12.7 / 4节的一部分:

  

...被调用的函数是构造函数中的最终覆盖   析构函数的类,而不是在更派生的类中重写它

对foo的调用在C::C()中,所以

  

...构造函数的类......

main(),因此

  

...不是一个在更派生的类中重写它。

(这将是B)。

你还提到“在一个明确的构造函数中,B已经完全构建了。”这条线...

C

不会改变第12.7 / 4节中提到的行为。 C仍称为C c; 的构造函数,因此

  

...被调用的函数是construtor或析构函数类中的最终覆盖。