12

I have a class like this:

class Inner;

class Cont
{
public:
    Cont();
    virtual ~Cont();
private:
    Inner* m_inner;
};

in the .cpp, the constructor creates an instance of Inner with new and the destructor deletes it. This is working pretty well.
Now I want to change this code to use auto_ptr so I write:

class Inner;

class Cont
{
public:
    Cont();
    virtual ~Cont();
private:
    std::auto_ptr<Inner> m_inner;
};

Now, the constructor initialized the auto_ptr and the destructor does nothing.

But it doesn't work. the problem seem to arise when I'm instantiating this class. I get this warning:

warning C4150: deletion of pointer to incomplete type 'Inner'; no destructor called

Well, this is obviously very bad and I understand why it happens, The compiler doesn't know about the d'tor of Inner when instantiating the template of auto_ptr<Inner>

So my question: Is there a way to use auto_ptr with a forward declaration like I did in the version that uses just plain pointers?
Having to #include every class I declare a pointer to is a huge hassle and at times, just impossible. How is this problem usually handled?

trompa
  • 1,907
  • 1
  • 17
  • 25
shoosh
  • 70,450
  • 50
  • 199
  • 310
  • I used to get this problem when I declared an empty destructor (or didn't declare it at all) in the header file of class Cont. From your example up there you seem to be doing it correctly but just wanted to highlight this prob. – rui Dec 23 '09 at 10:28
  • The d'tor is declared in the cpp, and I get the warning for the instantiation of the `auto_ptr` inside the class. – shoosh Dec 23 '09 at 10:31

7 Answers7

13

You need to include the header defining class Inner into the file where Cont::~Cont() implementation is located. This way you still have a forward declaration in teh header defining class Cont and the compiler sees class Inner definition and can call the destructor.

//Cont.h
class Inner; // is defined in Inner.h
class Cont 
{ 
    virtual ~Cont(); 
    std::auto_ptr<Inner> m_inner;
};

// Cont.cpp
#include <Cont.h>
#include <Inner.h>

Cont::~Cont()
{
}
sharptooth
  • 159,303
  • 82
  • 478
  • 911
  • I do that, but the warning points out that `Inner` needs to be defined at the line of the `auto_ptr` instantiation, inside the class. – shoosh Dec 23 '09 at 10:30
  • 1
    That's very strange. Is that Visual C++? – sharptooth Dec 23 '09 at 10:31
  • The solution above is exactly how we do it in VS2k3, VS2k5 and VS2k8. Very-very strange. Perhaps you could turn on generation of a preprocessed file and examine it. – sharptooth Dec 23 '09 at 10:45
  • @sharptooth Just to be clear, defining the destructor is not always needed. The only important thing is that the definition of `Inner` should be available at the point `Cont` is destroyed. Also, there is no need of a `virtual` (in the) destructor. – amit Dec 21 '11 at 10:23
  • @phaedrus: Yeap, except without a virtual destructor you still can bind a `auto_ptr` to `Derived*` and run into UB and not having the class definition at the point of object destruction is also UB and thus not required to be diagnosed by the compiler. – sharptooth Dec 21 '11 at 10:30
4

Turns out the problem occurs only when I make the c'tor inline. If I put the c'tor in the cpp, after the decleration of Inner everything's ok.

shoosh
  • 70,450
  • 50
  • 199
  • 310
3

It seems to be ridiculous but I solved the same issue by adding #include <memory> to the Cont.h file.

Antern
  • 43
  • 4
3

You may consider boost::shared_ptr() instead. It has no practical disadvantages instead of performance, and is much more friendly to forward declarations:

boost::shared_ptr<class NeverHeardNameBefore> ptr;

is okay, without extra declarations above.

shared_ptr does more than auto_ptr, such as reference counting, but it should not harm if you don't need it.

Pavel Radzivilovsky
  • 17,994
  • 3
  • 54
  • 65
  • Or `boost::scoped_ptr` which is closer except it even doesn't try to pretend your instances will be copyable. But then it still doesn't answer the question how one should go about using them with incomplete types (I think you'll need to define a destructor by which time the type is complete). – UncleBens Dec 23 '09 at 13:10
2

The forward declaration in the header is ok if you implement the destructor in the cont.cpp file and you include inner.h, as others pointed out.

The problem could be in the use of Cont. In each cpp that uses (and destroys) Cont you have to include cont.h AND inner.h. That solved the problem in my case.

jjth
  • 21
  • 2
0

This question (deleting object with private destructor) and this question (how to write iscomplete template) may be help you.

Community
  • 1
  • 1
Alexey Malistov
  • 24,755
  • 13
  • 60
  • 84
0

You aren't technically supposed to instantiate standard library templates with incomplete types, although I know of no implementation where this won't work. In practice, Sharptooth's answer is what I'd recommend also.

There really wasn't anything wrong with using a naked pointer for your impl pointer, as long as you call delete on it in your destructor. You should probably also implement or disable the copy constructor and assignment operator.

Brian Neal
  • 29,279
  • 6
  • 50
  • 59