strawhatsai
strawhatsai

Reputation: 39

I am attempting to define a class which can only be instantiated on the heap

I am trying to achieve this by overloading the delete operator as a friend function. The code is as follows:

#include <iostream>


class HeapOnly
{
    int x;
    int y;
    ~HeapOnly()
    {

    }
public:
    void friend operator delete(void* a);
};

void operator delete(void* p)
{
    delete p;
}

int main()
{
    HeapOnly *abc = new HeapOnly();
    delete abc;
}

The above code gives error in Visual Studio "Error C2248 'HeapOnly::~HeapOnly': cannot access private member declared in class 'HeapOnly"

I do not understand as to why it is giving error as the delete operator is overloaded as friend function hence it should be able to access the destructor which is in private.

Upvotes: 0

Views: 130

Answers (2)

Walter
Walter

Reputation: 45424

As why your attempt failed, see other answers. I don't know whether, and if so how, you can achieve the general goal of a class that can only be instantiated on the heap and if this is desirable, but I doubt both.

However, what you can do is to restrict de-allocation to go through std::allocator or std::default_delete, so that simple automatic variables are not possible:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>

class HeapOnly
{
    ~HeapOnly();
  public:
    bool operator < (HeapOnly const&) const;
    friend class std::allocator<HeapOnly>;
    friend class std::default_delete<HeapOnly>;
};

int main()
{
    std::vector<HeapOnly> X(10);   // okay
    HeapOnly Y;                    // ERROR: cannot be destructed
    std::sort(X.begin(),X.end());  // ERROR: requires automatic variables
    std::vector<std::unique_ptr<HeapOnly>> Z(10);    // okay
    for(auto&z:Z)
        z.reset(new HeapOnly);
    std::sort(Z.begin(),Z.end(),[](std::unique_ptr<HeapOnly> const&x,
                                   std::unique_ptr<HeapOnly> const&y)
                                { return *x < *y; }); 
}

Note, however, that many things rely on automatic variables, including std::swap and std::sort (befriending std::sort directly does not help, as it delegates the work to implementation-specific helper routines). However, you can enable unique_ptr (and shared_ptr), which appears the natural thing to do with a heap-only class, and sort the objects pointed to.

Note further that even though the code above may work with most if not all STL implementations, this is not guaranteed. std::allocator<>::destroy() may, for example, delegate the destruction to a helper function, which is an implementation detail and hence cannot be befriended.

However, you could test for this and use SFINAE to dodge such cases.

Upvotes: 2

user2486888
user2486888

Reputation:

There isn't really a "delete operator" for you to overload. There are:

  • Delete expression, as in delete abc; and
  • Deallocation function which deallocate memory, and its syntax is, confusingly, operator delete.

In other words, you have never overloaded the non-existent "delete operator" or declaring it a friend. The only thing overloaded and declared a friend is the deallocation function.

The delete expression does thing in two steps:

  1. Invoke the destructor to reduce the object back into raw memory
  2. Invoke the deallocation function to deallocate the memory

Therefore, the private destructor is still considered invoked directly by main, which is not a friend of the class.

We can do a little experiment to verify that:

Code 1: Without friend declaration: https://godbolt.org/g/WVmzNP

class HeapOnly
{
    int x;
    int y;
    ~HeapOnly() = default;
};

int main()
{
    HeapOnly *abc = new HeapOnly();
    delete abc;
}

Code 2: Declaring main as friend: https://godbolt.org/g/9PVZ4C

class HeapOnly
{
    int x;
    int y;
    ~HeapOnly() = default;

    friend int main();
};

int main()
{
    HeapOnly *abc = new HeapOnly();
    delete abc;
}

Upvotes: 2

Related Questions