tags:

views:

249

answers:

6

When I try compiling the following:

#include <iostream>

class Test
{
public:
    void* operator new (size_t num);
    void operator delete (void* test);
    ~Test();
private:
    Test();
};

Test::Test()
{
    std::cout << "Constructing Test" << std::endl;
}

Test::~Test()
{
    std::cout << "Destroying Test" << std::endl;
}

void* Test::operator new (size_t num)
{
    ::new Test;
}

void Test::operator delete(void* test)
{
    ::delete(static_cast<Test*>(test));
}

int main()
{
    Test* test = new Test;
    delete test;
}

I get :

$ g++ -o test test.cpp
test.cpp: In function ‘int main()’:
test.cpp:14: error: ‘Test::Test()’ is private
test.cpp:36: error: within this context

If the new is a member function, why can it not call the private constructor?

Edit: My idea is to create a class that can only be instantiated on the heap using totally standard syntax. I was hoping since new is a data member, it could call the private constructor but since new is not used for stack objects, you would not be allowed to create the object on the stack.

+5  A: 

new does not call the constructor - the compiler does, and the constructor must be accessible to it. Take this simpler code:

class A {
    A() {}
public:
    void * operator new( size_t x ) { return 0; }
};

int main() {
    A* a = new A;
}

Obviously, new is not calling the constructor, but you will still get a "private constructor" error message if you compile it..

anon
So, does this somehow illustrate why a private constructor cannot be called by the compiler?
That wasn't the question. But of course private constructors cannot be called by the compiler - hence the practice of making the copy constructor private to prevent call by value.
anon
+1  A: 

Constructors are really no different than other member function in this respect: If they're marked as private, they are inaccessible outside the class (such as in main). And since, during instantiation, a constructor always needs to be called, you cannot instantiate an object of a class that has only private (i.e. inaccessible) constructors.

Now, even if you do not declare any constructor yourself, the C++ compiler will provide certain default constructors, and even a default assignment operator =:

  • default constructor (having no arguments): Test::Test()
  • copy constructor (taking an object reference of the class's type): Test::Test(const Test&)
  • assignment operator =: Test& Test::operator =(const Test&)

And this is exactly where private constructors are useful: Sometimes, you do not want your class to have all of those implicit implementations, or you don't want your class to support certain behaviours such as copy assignment or copy construction. In that case, you declare those members that you do not want for your class as private, e.g.:

class Test
{
private:
    Test(Test& init) { }  // <-- effectively "disables" the copy constructor
}
stakx
A: 

The problem is that if the constructor is private, you cannot instantiate an object.

Why did you make your constructor private?

Liz Albin
I want an object that can be instantiated only on the heap. I was hoping that since the new operator is part of the class it would be able to call the private constructor.
doron
You could use a factory class for this. Make this the only place in the code where your objects can be instantiated. By nesting the factory class inside your `Test` class, it is allowed to call the `new` constructor even though it is private. Another option would be for your factory class to be a `friend` of your `Test` class.
stakx
Look into the "Named Constructor Idiom"... you just create a public static "Create..." method which can call your private constructor and create the instance on the heap only.
It is, the problem is that a scope outside your class - which is where it's instantiated - can't call the constructor as it's private.Since you only want to construct on the heap, how about writing a Create() or Clone() method. Alternatively, how about using smart pointers.
Liz Albin
+10  A: 

This is what you could do to force your object to be created on the heap:

class Foo {
public:
    static Foo *Create() {
         return new Foo;
    }
private:
    Foo() {}
};

and then when you use it:

Foo *foo = Foo::Create();

You might want to consider returning a shared_ptr instead of a raw pointer to help ensure that the object gets deleted.

This isn't technically what you asked for, but it is what you indicated that you would like to achieve...

villintehaspam
+7  A: 

I think you have a misunderstanding on what the operator new does. It does not create objects, but rather allocates memory for the object. The compiler will call the constructor right after calling your operator new.

struct test {
   void * operator new( std::size_t size );
};
int main()
{
   test *p = new test;
   // compiler will translate this into:
   //
   // test *p = test::operator new( sizeof(test) );
   // new (static_cast<void*>(p)) test() !!! the constructor is private in this scope
}

The main usage of the operator new is having a memory allocator different to the default allocator for the system (usually malloc), and it is meant to return an uninitialized region of memory on which the compiler will call the constructor. But the constructor is called after the memory is allocated in the scope where the new call was written (main in this case).

After acceptance note

The complete solution to the unformulated question: how do I force users of my class to instantiate in the heap? is to make the constructors private and offer a factory function, as shown in some other answers (as the one by villintehaspam) point out.

David Rodríguez - dribeas
Makes sense since new returns a void* (ie. unconstructed memory)
doron
+1  A: 

For the reasons badly outlined by various people what you are doing doesn't work; the way I ussually achieve your 'not on the stack' goal is like so

class HeapOnly {
  public:
    static HeapOnly* CreateInstance() { return new HeapOnly(); }
  protected:
    HeapOnly() { }
};

Now the only way to instantiate it is: HeapOnly *MyObj=HeapOnly::CreateInstance();

Elemental