0

在这个关于 Curiously Recurring Template Patten 的网页中,派生类可以在堆栈上实例化(对象计数器示例,其中基类模板具有受保护的析构函数):CRTP-wiki ..我自己编译。

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>
{
    // ...
};

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

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

所以,

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

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

提前致谢!

4

1 回答 1

1

我创建了一个示例,可以创建堆栈和堆上的对象。我认为答案是错误的,保护析构函数在您的代码段中的注释中具有确切的含义:“永远不应该通过这种类型的指针删除对象”。此外,如果将析构函数设为虚拟,则可以跳过“受保护”(因为 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;
}
于 2017-08-02T14:40:10.017 回答