受保护的析构函数禁用在堆栈上创建派生类的对象?

时间:2017-08-02 07:48:49

标签: c++ crtp class-template

在这个关于Curiously Recurring Template Patten的网页中,派生类可以在堆栈上实例化(Object Counter示例,其中基类模板具有受保护的析构函数):https://www.fmsinc.com/MicrosoftAccess/Forms/Synchronize/LinkedSubforms.asp ..我自己编译。

template <typename T>
struct counter
{
    static int objects_created;
    static int objects_alive;

    counter()
    {
        ++objects_created;
        ++objects_alive;
    }

    counter(const counter&)
    {
        ++objects_created;
        ++objects_alive;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
        --objects_alive;
    }
};
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );

class X : counter<X>
{
    // ...
};

class Y : counter<Y>
{
    // ...
};

但是这个答案说使基类的析构函数受到保护会禁止在堆栈上实例化派生类:CRTP-wiki

  

正如已经回答的那样,Poco :: RefCountedObject具有受保护的析构函数,因此无法在堆栈上创建从中继承的所有类....

所以,

(1)这个答案错了​​吗?还是我误解了?

(2)为什么CRTP示例会对析构函数进行保护?是否意味着禁止在堆栈上实例化基类模板的特化?我可以在堆上实例化基类模板的特化(我试过,我不能,但不知道为什么)?

提前致谢!

1 个答案:

答案 0 :(得分:1)

我创建了一个示例,可以创建堆栈和堆上的对象。 我认为答案是错误的并且保护析构函数具有在您的代码段中的注释中给出的完全含义:“永远不应该通过这种类型的指针移除对象”。如果析构函数是虚拟的,那么可以跳过'protected'(因为delete总是先调用继承的析构函数)。我希望我在这里没有错过任何东西。

#include <cstdio>

template <typename T>
struct counter
{
    static int objects_created;
    static int objects_alive;

    counter()
    {
        ++objects_created;
        ++objects_alive;
    }

    counter(const counter&)
    {
        ++objects_created;
        ++objects_alive;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
        --objects_alive;
    }
};
template <typename T> int counter<T>::objects_created(0);
template <typename T> int counter<T>::objects_alive(0);

class X : counter<X>
{
public:
    X()
    {
        printf("Hello from X %dth instance, %d still up\n", objects_created, objects_alive);
    }

    ~X()
    {
        printf("Bye X\n");
    }
};

int main()
{
    {
        X x; // hello x1
        {
            X x2; // hello x2
        } // bye x2

        X* x3 = new X(); // hello x3
        X* x4 = new X(); // hello x4

        delete x3; // bye x3

        counter<X>* x5 = (counter<X>*)x4;

        delete x5;  // destructor is inaccesible, does not compile
    }
    return 0;
}