C++ c++;CRTP堆栈损坏
在MSVC 2013 x64 Debug config上运行以下代码时,退出main()函数时会显示一个消息框,其中包含此著名的错误消息C++ c++;CRTP堆栈损坏,c++,crtp,stack-corruption,C++,Crtp,Stack Corruption,在MSVC 2013 x64 Debug config上运行以下代码时,退出main()函数时会显示一个消息框,其中包含此著名的错误消息 "Run-Time Check Failure #2 - Stack around the variable 'tmp' was corrupted.". 我无法回答的问题是:为什么 请注意,在Release config上运行时不会出现错误消息。(为什么?) 免责声明:这只是一个示例代码,这意味着我正试图在其他类(一个基本类和几个派生类)上使用相同的设计
"Run-Time Check Failure #2 - Stack around the variable 'tmp' was corrupted.".
我无法回答的问题是:为什么
请注意,在Release config上运行时不会出现错误消息。(为什么?)
免责声明:这只是一个示例代码,这意味着我正试图在其他类(一个基本类和几个派生类)上使用相同的设计,这些类具有更多的方法和模板参数,并且具有比基本int*更复杂的数据类型
#include <iostream>
template <class T>
class base {
public:
base() {
static_cast<T*>(this)->setData();
}
~base() {
static_cast<T*>(this)->destroyData();
}
void print() {
static_cast<T*>(this)->print_int();
}
};
#包括
模板
阶级基础{
公众:
base(){
静态_cast(this)->setData();
}
~base(){
静态_cast(this)->销毁数据();
}
作废打印(){
static_cast(this)->print_int();
}
};
派生类:公共基{
公众:
void setData(){
x=新的int();
}
void数据(){
删除x;
x=零PTR;
}
无效打印_int(){
std::cout print_int();}
};
class-foo
{
公众:
void setData()
{
x=新的int();
}
void数据()
{
删除x;
x=零PTR;
}
无效打印_int()
{
std::couttmp
是一个base
,而不是派生的。CRTP的要点是基类“知道”对象的实际类型,因为它是作为模板参数传递的,但是如果手动创建基
,则基类函数会认为该对象是派生的
,但它不是-它只是一个基
。因此会发生奇怪的事情(未定义的行为)。(如另一个答案中所述,您也在打印某些内容,但没有设置它…)
至于您的第二个问题,MSVC生成的用于检测此类程序员错误的检查似乎在发布模式下被关闭,可能是出于性能原因。至于main()
中显示的代码:
注意:基类不应该从基类构造函数中调用依赖于完全初始化的派生的类方法的任何方法,正如您的示例所示(static_cast(this)->setData();
)
您可以存储对T*
的引用,以供以后使用,如上面的示例所示
派生类:公共基{
公众:
派生的(){
setData();
}
void数据(){
删除x;
x=零PTR;
}
无效打印_int(){
std::cout模板类混合在:public T{
公众:
base(){(this)->setData();}
~base(){(this)->destroyData();}
void print(){(this)->print_int();}
};
将derived
重命名为foo
。不要继承foo
中的mix\u或base
将base
交换为main
中的mix\u
CRTP并不是所有问题的解决方案。你在评论中说
派生类永远不会被实例化,其构造函数永远不会被调用
其他人已经指出了未构造对象的未定义用途。具体地说,考虑<代码> int *x/c> >是<代码>派生的/<代码>成员,并且使用顺序生成调用:<代码>派生::SETYDATA()/<代码>,<代码>派生::Print()/代码>,并在退出<代码>派生::
,不存在的对象的所有成员函数,指从未为其分配过空格1的成员
但我认为你所追求的是完全合法的。分解代码是模板和继承的全部要点,也就是说,通过抽象出样板文件,使重要代码更容易识别,更不用说理解,使以后的重构更容易进行
因此:
模板
struct preconstruction\u access\u subset{};//通常为空
模板
结构基:S{
base(){S::setData();}
~base(){S::destroyData();}
void print(){S::print_int();}
};
//……以后。。。
#包括
结构派生;
模板结构预构造访问子集{
//“base”中的所有结构器都需要
int*x;
void setData(){x=new int;}
void destroyData(){delete x;}
无效打印_int(){std::cout除了下面显示的问题之外,在派生的
的构造函数中将x
设置为jibberish的语言是自由的吗?在派生的
的构造函数运行之前进行静态转换是否合法?@jthill'这个问题似乎离题了…'不,这太简单了!我标记了你的评论(和结束提议)没有建设性!@jthill easy,easy。你不知道我花了多少天试图理解为什么会发生这种问题,我读了多少帖子/论坛。就像πάντα一样ῥεῖ 我很尊重stackoverflow和在这里做出贡献的人。我只在我真的没有其他选择的时候才在这里问问题(我周围没有人知道答案或者我在网络搜索中找不到匹配的答案)@Yakk,通过像我所做的那样实例化基类,派生类的构造函数将不会被调用。我只是在调试时才意识到这一点。这就是为什么我在基类的构造函数上调用setData方法的原因。这是我无数次尝试使此代码正常工作的一部分。到目前为止,我失败了。@πνταῥεῖ & OP:看看这个和到目前为止的答案,我发现它不是看起来的那样,但让我指出我评论中的最后一个词:“显示”。如果不打赌这篇文章这次会有所不同,就不可能把它和几十篇完全相同的浪费时间的文章区分开来,我想经过深思熟虑,你会同意这篇文章目前的形式
class derived : public base<derived> {
public:
void setData() {
x = new int();
}
void destroyData() {
delete x;
x = nullptr;
}
void print_int() {
std::cout << "x = " << *x << std::endl;
}
private:
derived() {}
derived(const derived& other) {}
inline derived& operator= (derived copy) {}
int *x;
};
int main() {
base<derived> tmp;
tmp.print();
return 0;
}
#include <iostream>
template <class T>
class mix_in : public T
{
public:
mix_in() { (this)->setData(); }
~mix_in() { (this)->destroyData(); }
void print() { (this)->print_int(); }
};
class foo
{
public:
void setData()
{
x = new int();
}
void destroyData()
{
delete x;
x = nullptr;
}
void print_int()
{
std::cout << "x = " << *x << std::endl;
}
foo() {}
private:
int *x;
};
int main()
{
mix_in<foo> tmp;
tmp.print();
return 0;
}
int main() {
base<derived> tmp;
tmp.print();
return 0;
}
template <class T>
class base {
public:
~base() {
static_cast<T*>(this)->destroyData();
}
// ...
protected:
T* thisPtr;
base() : thisPtr(static_cast<T*>(this)) {
}
};
class derived : public base<derived> {
public:
derived() {
setData();
}
void destroyData() {
delete x;
x = nullptr;
}
void print_int() {
std::cout << "x = " << *x << std::endl;
}
private: // ????
derived(const derived& other) {}
inline derived& operator= (derived copy) {}
int *x;
};
template <class T> class mix_in:public T {
public:
base() { (this)->setData(); }
~base() { (this)->destroyData(); }
void print() { (this)->print_int(); }
};
template < class T >
struct preconstruction_access_subset {}; // often left empty
template < class T, class S = preconstruction_access_subset<T> >
struct base : S {
base() { S::setData(); }
~base() { S::destroyData(); }
void print() { S::print_int(); }
};
// ... later ...
#include <iostream>
struct derived;
template<> struct preconstruction_access_subset<derived> {
// everything structors in `base<derived>` need
int *x;
void setData() { x = new int; }
void destroyData() { delete x; }
void print_int() { std::cout << x << '\n'; }
};
struct derived : base<derived> {
// everything no structors in any base class need to access
};
int main() {
base<derived> tmp;
tmp.print();
}