c ++ CRTP堆栈损坏

lao*_*uca 2 c++ crtp stack-corruption

当在MSVC 2013,x64 Debug config上运行以下代码时,它会在退出main()函数时显示一个带有此着名错误消息的消息框

"Run-Time Check Failure #2 - Stack around the variable 'tmp' was corrupted.". 
Run Code Online (Sandbox Code Playgroud)

我无法回答的问题是:为什么?

请注意,在Release配置上运行时不会出现任何错误消息.(为什么?)

免责声明:这只是一个示例代码,这意味着我试图在其他类(一个基础和几个派生)上使用相同的设计,使用更多的方法和模板参数,并且使用比基本的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();
    }
};
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)
int main() {
    base<derived> tmp;

    tmp.print();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑: @Yakk如果我理解正确,你建议解决这个代码:

#include <iostream>

template <class T>
class mix_in : public T
{
public:
    mix_in() { (this)->setData(); }
    ~mix_in() { (this)->destroyData(); }
    void print() { (this)->print_int(); }
};
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)
int main()
{
    mix_in<foo> tmp;

    tmp.print();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它工作得很好,谢谢.我可能会使用这种模式,因为似乎没有解决方案将CRTP用于我正在尝试的事情.

但我仍然想了解堆栈损坏发生的原因.尽管围绕使用的所有讨论都使用CRTP进行所有事情,但我想非常清楚地理解它为什么会发生.

再次感谢你.

πάν*_*ῥεῖ 6

至于您的代码如下所示main():

int main() {
    base<derived> tmp;

    tmp.print();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

base<derived> tmp;你想要的是这种模式的错误用法derived tmp;.

CRTP的要点是派生类被注入到基类中,并为在编译时解析的某些功能提供实现.
基类本质上是预期的,仅通过继承进行实例化.因此,您应该确保在继承上下文之外不要有任何实例化.

为了避免被这种误解困住的实现用户,make base的构造函数受到保护:

template <class T>
class base {
public:
    ~base() {
        static_cast<T*>(this)->destroyData();
    }

// ...
protected:
    T* thisPtr;
    base() : thisPtr(static_cast<T*>(this)) {
    }
};
Run Code Online (Sandbox Code Playgroud)

注意:基类不应该调用依赖于derived基类构造函数中的完全初始化类方法的任何方法,正如您在sample(static_cast<T*>(this)->setData();)中所示!

您可以存储该引用以T*供以后使用,如上面的示例所示.


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;
};
Run Code Online (Sandbox Code Playgroud)

还有点不清楚,为什么你要为你的班级隐藏复制构造函数和赋值运算符?