具有奇怪重复模板模式的派生类中的损坏的成员变量

时间:2018-11-06 01:02:18

标签: c++ polymorphism crtp

我目前正在使用CRTP,遇到了一个问题,即派生类中的成员变量被损坏,也就是具有垃圾值(目前有4种多态性,其中最高级的基类称为“ A” ”和最底层的派生类“ D”)。

以下代码显示了此问题的示例:

//A.hpp
template <class TB>
class A {
public:
    A();
    void CRTP_func();
};

template <class TB>
A<TB>::A() {
    std::cout << "A constructor called!" << std::endl;
}

template<class TB>
void A<TB>::CRTP_func() {
    std::cout << "CRTP_index called in A" << std::endl;
    static_cast<TB*>(this)->CRTP_func2();
}

//B.hpp
#include "A.hpp"
#include <vector>

template<class TC>
class B : public A<B<TC>>
{
public:
    B();
    void CRTP_func2();
};

template<class TC>
B<TC>::B() {
    std::cout << "B constructor called!" << std::endl;
}

template<class TC>
void B<TC>::CRTP_func2() {
    std::cout << "CRTP_func called in B" << std::endl;
    static_cast<TC*>(this)->CRTP_func3();
}

//C.hpp
#include "B.hpp"

template<class TD>
class C : B<C<TD>> {
public:
    C();
    void CRTP_func3();
    int x;
};

template<class TD>
C<TD>::C() {
    std::cout << "C constructor called" << std::endl;
}

template<class TD>
void C<TD>::CRTP_func3() {
    std::cout << "CRTP_index3 called in C" << std::endl;
    static_cast<TD*>(this)->CRTP_func4();
}


//D.hpp
#include "C.hpp"

    class D : C<D> {
    public:
        D();
        bool onInit();
        void CRTP_func4();
        C<D> top;
        int y = 0;

    };

D::D() {
    std::cout << "D constructor called!" << std::endl;
}

bool D::onInit() {
    std::cout << "D onInit called!" << std::endl;
    y = 5;
    return true;
}

void D::CRTP_func4() {
    std::cout << y << std::endl;
    std::cout << "CRTP_index4 called in D! " << std::endl;
}

//main.hpp
int main {
D * D_ptr = new D();
    D_ptr->onInit();
    D_ptr->top.CRTP_func3();
    return 0;
}

您可以看到A是基类,而D是派生类,

A<B<C<D>>>

该程序的输出如下:

A constructor called!
B constructor called!
C constructor called
A constructor called!
B constructor called!
C constructor called
D constructor called!
D onInit called!
CRTP_index3 called in C
-33686019
CRTP_index4 called in D!

在D.hpp中打印出值-33686019,其中打印了值y,并在初始化时将其设置为5。经过一番挖掘后,我检查了main.cpp中的值,即使在进行了这些CRTP调用后也将其设置为5,但仍会打印出垃圾值。

经过更多调试后,我意识到删除该行

int x;
B.hpp中的

解决了此问题,所以我认为该问题与未对准有关,但是我不确定为什么会发生这种情况。有谁知道为什么会发生这种情况或如何解决?

很抱歉,文章过长且模棱两可,我试图消除大部分复杂性并尽可能简化代码以简化文章。

更新:

由于下面的评论,我找出了解决问题的方法。代替使用D::top,更好的方法是在主文件中这样创建一个指针:

C<D> * C_ptr = static_cast<C<D>*>(D_ptr);

,然后从那里像这样调用CRTP_func3()

C_ptr->CRTP_func3();

这按预期工作。

2 个答案:

答案 0 :(得分:2)

您在静态类型为console.log($scope.hideDiv) // Is false {{hideDiv}} <!--Is false--> CRTP_func3())的对象上调用函数C<D>。函数D::top执行C<D>::CRTP_func3(),但是对象没有预期的类型。因此,行为是不确定的。

答案 1 :(得分:0)

从逻辑上讲,您遇到的最基本问题是,您期望D_PtrD_Ptr->top的值与y相同(您说期望值为5)。 D_Ptr->top是一个完全不同的实例,即使最终从D派生,也将拥有自己的y副本。

然后,D是从C派生的,因此,CD派生是根本不可能的,而与模板的疯狂无关。这就是您通过在C的CRTP_func4指针上调用this来做出的假设。

此外,同一函数调用假定模板类型TDD的实例。该函数调用存在于C中,这是C做出的疯狂假设-尽管我认为在这种情况下它是正确的。 (那个如果不是,则编译器会捕获)

最后关于crtp的问题:考虑拒绝撒但及其一切方式。

但是,严重的是,显然没有完整的替代方法,但是我想您会发现,如果您充分考虑了接口的功能(或C ++中的pure abstract classes),您可能会 能够找到使用它的方法。并且具有(几乎)相同的性能...当然,我不知道您的特定问题,但是我强烈建议您仔细阅读这篇文章https://en.wikipedia.org/wiki/Composition_over_inheritance

尤其要看第二个示例代码块,它是用C#编写的(其中interface将是C ++中的pure abstract class)。考虑一下这种模式是否可以帮助您。