Leedehai
Leedehai

Reputation: 3960

Protected destructor disables creating derived class's objects on stack?

In this webpage about Curiously Recurring Template Patten, the derived class can be instantiated on the stack (the Object Counter example, in which the base class template has a protected destructor): CRTP-wiki.. i compiled myself.

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

But this answer says making the base class's destructor protected will prohibit instantiating derived class on stack: answer:

As answered already, Poco::RefCountedObject has protected destructor, so all classes inheriting from it can not be created on the stack....

So,

(1) is the this answer wrong? Or did I misunderstood?

(2) Why does the CRTP example make the destructor protected? Is it meant to prohibit instantiating a specialization of the base class template on stack? Can I instantiate a specialization of the base class template on heap (I tried, I can't, but don't know why)?

Thanks in advance!

Upvotes: 0

Views: 262

Answers (1)

R2RT
R2RT

Reputation: 2146

I've created an example and objects on both stack and heap can be created. I think the answer is wrong and protecting the destructor has exactly meaning given in comment in your snippet: "objects should never be removed through pointers of this type". Also, 'protected' could be skipped if destructor were made virtual (since delete would always call inherited destructor first). I hope I didn't miss something here.

#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;
}

Upvotes: 1

Related Questions