为什么构造函数中的dynamic_cast(this)无法正常工作,以及如何使其正常工作?

时间:2018-11-12 16:05:06

标签: c++

我正在使用CRTP将D1D2的常见实现转移到模板中,但是,在构造对象时,我必须调用特定于每种类型的重载函数。

以下代码的输出是

D 0x7ffc7d370538
D1 0
D 0x7ffc7d370540
D2 0

预期输出为

D 0x7ffc7d370538
D1 0x7ffc7d370538
D 0x7ffc7d370540
D2 0x7ffc7d370540

为什么dynamic_cast返回nullptr?如何修复此代码?

#include <iostream>

template <typename Derived>
struct B {
    B();
    virtual ~B() {}
};

struct D1 : B<D1> {
};

struct D2 : B<D2> {
};

void use(D1* d) { std::cout << "D1 " << d << std::endl; }
void use(D2* d) { std::cout << "D2 " << d << std::endl; }

template <typename Derived>
B<Derived>::B() {
    std::cout << "D " << this << std::endl;
    Derived* derivedThis = dynamic_cast<Derived*>(this);
    use(derivedThis);
}

int main() {
    D1 d1;
    D2 d2;
}

我发现的唯一解决方法是以下

#include <iostream>

template <typename Derived>
struct B {
    B();
    virtual ~B() {}
};

struct D1;
struct D2;

void use(D1* d) { std::cout << "D1 " << d << std::endl; }
void use(D2* d) { std::cout << "D2 " << d << std::endl; }

struct D1 : B<D1> {
    D1() { use(this); } # code duplication
};

struct D2 : B<D2> {
    D2() { use(this); } # code duplication
};

template <typename Derived>
B<Derived>::B() {
    std::cout << "D " << this << std::endl;
}

int main() {
    D1 d1;
    D2 d2;
}

但是,在我的场景中,代码重复太多,无法重复调用函数use。例如,可以有许多派生类,或许多对类似于use的函数的调用。

2 个答案:

答案 0 :(得分:5)

造成麻烦的直接原因是,在构造派生类之前调用​​基类构造函数。因此,在构造函数中通过dynamic_class进行强制转换会导致nullptr

没有办法解决,您不能使用基类构造函数派生类的对象。

另一方面,在同一类中同时使用运行时和编译时多态时,通常是设计气味。坚持其中任何一个。

答案 1 :(得分:0)

在此代码中

template <typename Derived>
B<Derived>::B() {

您将要初始化B<Derived>的实例。实际上,这是在初始化B<D1>实例的D1基类子对象或B<D2>实例的D2基类子对象。

在成功创建基类子对象之后,D1D2对象都不存在。这意味着在创建基类子对象期间,派生类对象还不存在,同样,基类对象 的动态类型不能为派生类型。

如果您想获得有关如何构造代码的建设性建议,则确实需要解释use的实际用途。