具有奇怪重复模板模式的派生类中的成员变量已损坏
Corrupt member variable in derived class with Curiously Recurring Templating Pattern
我目前正在玩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!
值-33686019在D.hpp中打印出来,其中打印值y,初始化时设置为5。经过一点挖掘,我检查了main.cpp中的值,即使在进行了这些CRTP调用之后,它也被设置为5,但会打印出一个垃圾值。
经过更多的调试,我意识到删除线路
int x;
在B.hpp中解决了这个问题,所以我认为这个问题与一些错位有关,但我不确定为什么会发生这种情况。有人知道为什么会发生这种情况或如何解决吗?
很抱歉帖子太长,代码不明确,为了帖子,我尽量去掉大部分复杂性,简化代码。
更新:
感谢下面的评论,我找到了解决问题的方法。与其使用D::top
,更好的方法是在主文件中创建一个指针,如下所示:
C<D> * C_ptr = static_cast<C<D>*>(D_ptr);
然后从那里这样调用CCD_ 3:
C_ptr->CRTP_func3();
这符合预期。
在静态类型为C<D>
(D::top
(的对象上调用函数CRTP_func3()
。函数C<D>::CRTP_func3()
执行static_cast<D*>(this)
,但对象没有预期的类型。因此,行为是未定义的。
从逻辑上讲,您遇到的最基本的问题是,您期望D_Ptr
和D_Ptr->top
对y
具有相同的值(您说过您期望5(。D_Ptr->top
是一个完全不同的实例,即使它最终派生自D
,也会有自己的y
副本。
然后,D
是从C
派生出来的,所以C
根本不可能从D
派生出来,无论模板是否疯狂。这是您通过在C.的this
指针上调用CRTP_func4
所做的假设
此外,相同的函数调用假定模板类型TD
是D
的实例。该函数调用存在于C
中,对于C
来说,这是一个疯狂的假设——尽管我认为在这种情况下它恰好是正确的。(如果不是,编译器会捕获(
最后关于crtp:考虑拒绝撒旦和他的所有方式。
但说真的,显然没有完全的替代方案,但我想你会发现,如果你充分考虑接口(或C++中的pure abstract classes
(的强大功能,你可能能够找到使用它的方法。并且具有(几乎(相同的性能。。。当然,我不知道你的具体问题,但我强烈建议你仔细阅读这篇文章https://en.wikipedia.org/wiki/Composition_over_inheritance
特别是第二个示例代码块,它是用C#编写的(其中interface
将是C++中的pure abstract class
(。思考一下这种模式是否能对你有所帮助。
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 如何使用基类指针引用派生类成员
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 在 C++ 中用派生类型重写成员函数
- 如果基类包含双指针成员,则派生类的构造函数
- 在派生类中绑定非静态模板化成员函数
- 为什么基指针可以访问虚拟函数中的派生成员变量
- 在销毁派生成员之前,在破坏者中调用共同功能
- 带有派生成员的基类的独特指针
- 为什么基类对象的私有成员仍然从派生成员中分配
- 派生成员函数指针的多态性
- 派生成员和重载Ostream运算符
- 通过指向派生类的指针访问派生成员变量
- 在构造过程中是否有一种简洁的方法可以派生成员的类型?
- 如何使用派生类特定的方法:派生类中的派生成员
- 获取从基类指针到具有不同类型的两个派生成员变量的访问权
- 当我使用根据基类定义的成员函数指针时,编译器如何调用派生成员函数?
- 将luabind派生成员调用为协同程序
- C++ POD 结构继承?是否有任何关于派生成员的内存布局的保证