“this”的dynamic_cast构造函数内部

发布于 2024-11-15 06:26:16 字数 1563 浏览 5 评论 0原文

这个问题与这个问题非常相似为什么我不能dynamic_cast在多重继承期间“横向”?,除了强制转换确实有效 - 只是不在构造函数内部。

头:

class A  
{  
public:  
    virtual                ~A() {}
    void                    printA();
};

class B
{
public:
                            B();
    virtual                ~B() {}
    void                    printB();

private:
    std::string             message_;
};

class C : public A, public B
{
public:
                        C() {}
    virtual                ~C() {}
};

Source:

void A::printA() { cout << "A" << endl; }
B::B()
{
    A* a = dynamic_cast< A* >( this );
    if ( a ) {
        message_ = std::string( "A and B" );
    } else {
        message_ = std::string( "B" );
    }
}
void B::printB() { cout << message_.c_str() << endl; }

Main:

int main( int argc, char* argv[] )
{
    cout << "Printing C..." << endl;
    C c;
    c.printA();
    c.printB();

    cout << "Checking again..." << endl;
    cout << !!dynamic_cast< A* >( &c ) << endl;

    return EXIT_SUCCESS;
}

结果:

Printing C...
A
B
Checking again...
1

因此,dynamic_cast 确实适用于多重继承(这并不奇怪!),但为什么在运行时为 B::B() 内的“this”指针调用时不呢?我认为该对象一旦进入构造函数体内就已完全形成,即所有内存都已分配给组件对象,但它们尚未初始化。我明白这取决于超类构造函数的顺序,但在这个例子中,A 在 B 之前被调用。

我显然不明白幕后到底发生了什么,有人可以启发我吗?

谢谢, 卡姆·班伯.

This question is very similar to this one Why can't I dynamic_cast "sideways" during multiple inheritence?, except that the cast does work - just not inside in the constructor.

Header:

class A  
{  
public:  
    virtual                ~A() {}
    void                    printA();
};

class B
{
public:
                            B();
    virtual                ~B() {}
    void                    printB();

private:
    std::string             message_;
};

class C : public A, public B
{
public:
                        C() {}
    virtual                ~C() {}
};

Source:

void A::printA() { cout << "A" << endl; }
B::B()
{
    A* a = dynamic_cast< A* >( this );
    if ( a ) {
        message_ = std::string( "A and B" );
    } else {
        message_ = std::string( "B" );
    }
}
void B::printB() { cout << message_.c_str() << endl; }

Main:

int main( int argc, char* argv[] )
{
    cout << "Printing C..." << endl;
    C c;
    c.printA();
    c.printB();

    cout << "Checking again..." << endl;
    cout << !!dynamic_cast< A* >( &c ) << endl;

    return EXIT_SUCCESS;
}

Result:

Printing C...
A
B
Checking again...
1

So, the dynamic_cast does work for multiple inheritance (no surprises there!), but why not when called at runtime for the 'this' pointer inside B::B()? I thought that the object was fully formed once inside the body of the constructor i.e. all the memory was allocated for the component objects, they haven't been initialised yet. I appreciate that this depends on the superclass constructor order, but in this example A is called before B.

I am obviously not understanding what exactly is happening under the hood, can someone please enlighten me?

Thanks,
Cam Bamber.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

缘字诀 2024-11-22 06:26:16

基本上,标准说它在对象的构造过程中不起作用(dynamic_cast)。
<引用>

编辑:根据下面的 VJo 评论添加。

注意:使用动态转换从“B”到“A”的转换应该有效,因为我们正在转换类型“C”的对象。如果我们将以下代码添加到 main: ,

B  bObj;
B& bRef = c;
B* bPtr = &c;
std::cout << !!dynamic_cast<A*>(&bObj) << std::endl;
std::cout << !!dynamic_cast<A*>(&bRef) << std::endl;
std::cout << !!dynamic_cast<A*>( bPtr) << std::endl;

额外的输出将是:

0   // Can not convert a B to an A
1   // Can convert this B to an A because it is really a C.
1   // This is  what we are reeling doing in B::B() that fails
    // It is not the dynamic_cast<> that fails but the conversion of this from C* to B*
    // That is causing UB

由于对象未完全形成,它在构造函数中失败。使用它,我们尝试在 C 构造函数启动(用户定义的代码)之前将 C 指针转换为 B 指针。因此,当dynamic_cast<>在B::B()中使用this作为指向C对象的指针时,就会失败。由于 UB 的原因,它无法执行您想要的操作。

12.7 构造和销毁[class.cdtor]

第3段

显式或隐式地将引用类 X 的对象的指针(泛左值)转换为指向 X 的直接或间接基类 B 的指针(引用),X 的构造及其所有直接的构造直接或间接从 B 派生的间接基类或间接基类应已开始,并且这些类的销毁不应完成,否则转换会导致未定义的行为。要形成指向对象 obj 的直接非静态成员的指针(或访问其值), obj 的构造应已开始且其销毁不应完成,否则指针值的计算(或访问成员)值)导致未定义的行为。

[示例:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X 
{ 
    E() : D(this),  // undefined: upcast from E* to A*
                    // might use path E* → D* → A* 
                    // but D is not constructed 
                    // D((C*)this), 
                    // defined: 
                    // E* → C* defined because E() has started 
                    // and C* → A* defined because
                    // C fully constructed 
      X(this) { // defined: upon construction of X,
                    // C/B/D/A sublattice is fully constructed
      } 
};

-结束示例]

Basically the standard says it will not work (dynamic_cast) during construction of an object.
<quote>

Edit: Added based on VJo comment below.

Note: The cast from a 'B' to an 'A' using dynamic cast should work because we are casting an object of type 'C'. If we added the following code to main:

B  bObj;
B& bRef = c;
B* bPtr = &c;
std::cout << !!dynamic_cast<A*>(&bObj) << std::endl;
std::cout << !!dynamic_cast<A*>(&bRef) << std::endl;
std::cout << !!dynamic_cast<A*>( bPtr) << std::endl;

The extra output would be:

0   // Can not convert a B to an A
1   // Can convert this B to an A because it is really a C.
1   // This is  what we are reeling doing in B::B() that fails
    // It is not the dynamic_cast<> that fails but the conversion of this from C* to B*
    // That is causing UB

It fails in the constructor because the object is not fully formed. Using this we are trying to convert a C pointer into a B pointer before the C constructor has started (the code defined by the user). Thus the use of this in B::B() as a pointer to a C object fails thus when the dynamic_cast<> is called on this it fails to do what you want it to because of UB.

12.7 Construction and destruction [class.cdtor]

Paragraph 3

To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.

[ Example:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X 
{ 
    E() : D(this),  // undefined: upcast from E* to A*
                    // might use path E* → D* → A* 
                    // but D is not constructed 
                    // D((C*)this), 
                    // defined: 
                    // E* → C* defined because E() has started 
                    // and C* → A* defined because
                    // C fully constructed 
      X(this) { // defined: upon construction of X,
                    // C/B/D/A sublattice is fully constructed
      } 
};

— end example ]

</quote>

往昔成烟 2024-11-22 06:26:16

每个基类构造函数都在派生类构造函数之前执行,并且在B构造函数期间,对象的动态类型为B;在您输入 C 构造函数之前,它不会变成 C。因此,您无法执行任何需要 C 动态类型的操作:您无法交叉转换到任何 C 的其他基类,并且如果您调用了虚函数,那么您将不会获得 C 提供的任何覆盖。

在底层,动态类型(至少在大多数实现中)由对象中的指针(称为“vptr”)确定,该指针指向一些指定类属性的静态数据,包括虚函数表(称为“vtable”)以及 dynamic_casttypeid 所需的信息。在每个构造函数之前,都会更新它以指向当前正在构造的类的信息。

Each base class constructor is executed before the derived class constructor, and during the B constructor, the dynamic type of the object is B; it does not become a C until you enter the C constructor. So you cannot do anything that requires a dynamic type of C: you can't cross-cast to any of Cs other base classes, and if you called a virtual function, then you would not get any overrides provided by C.

Under the hood, the dynamic type is (in most implementations at least) determined by a pointer in the object (known as the "vptr"), which points to some static data specifying properties of the class, including a table of virtual functions (known as the "vtable") and the information needed for dynamic_cast and typeid. Before each constructor, this is updated to point to the information for the class currently under construction.

掌心的温暖 2024-11-22 06:26:16

在构建 A 期间,动态类型无论如何都是 A。这是因为您将在构造派生类之前开始调用派生类的成员函数并访问派生成员变量,这将是 UB 并且非常糟糕。

During the construction of A then the dynamic type is A regardless. This is because you would start calling member functions of derived classes and accessing derived member variables before it's been constructed, which would be UB and very bad.

雨轻弹 2024-11-22 06:26:16

由于 B 不是从 A 继承(B 是最父类),因此 B 的动态类型在其构造函数中是B。只有当 AB 父级都构造完成后,才能构造子级 C,从而允许横向 dynamic_casting 。

Since B doesn't inherit from A (B is parent-most class), the dynamic type of B during its constructor is B. Only when both the A and B parents are constructed can the child C be constructed, allowing for sideways dynamic_casting.

冷了相思 2024-11-22 06:26:16

它在 B 内部不起作用,因为 B 不继承自 A

It doesn't work inside B, because B doesn't inherit from A

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文