So as part of a large hobby learning project I have implemented a mostly complete smart pointer implementation. It does practically every thing I ask of it, except for one minor detail that may prove to be a deal-breaker if I can't solve it. Contrived Example:
//Header1.h
#include <Header2.h>
class A
{
//Methods and such that involve class B in return type / arguments
};
//Header2.h
class A; //Forward declaration of A, needed because A includes Header2.h
class B
{
public:
SmartPointer<A> Ptr;
};
The previous code, as you could guess, gives me warning C4150: deletion of pointer to incomplete type 'type'; no destructor called
. I know why this is happening; in Header2.h
, the smart pointer code includes a delete on a forward declared instance of A
. If I could include Header1.h
, no problem. I don't really wish to have to refactor at this point.
I have heard the boost smart pointer has this problem solved, somehow. Bringing in boost is not the intent of this project, as it is pretty much a hobby / learning project. So how does boost deal with this issue? How could I get the smart pointer to behave, in this instance, like a raw pointer? 开发者_开发知识库 I have a few ideas, but I figured floating the question to SO could cull the list of ideas into a useful subset.
Forward declaring my thanks for helping me solve this one.
So how does boost deal with this issue?
Boost deals with this issue by using checked_delete
instead of delete
inside the smart pointer class template, thus requiring the complete definition of A
.
Boost maintains a pointer to a function which can be used to delete the object. It stores this in the pointer object along with the actual pointer. You can pass your own destructor if you want to thus doing something else besides calling delete.
The shared_ptr constructor is a template which gets a pointer to a template function which delete the objects. Since it is done in the constructor in a template the class only has to be complete when the object is being constructed. All other operations can be performed without that full access.
This can be resolved by defining the destructor (even an empty one) in class B's CPP file, like this:
//Header1.h
class A
{
//Methods and such that involve class B in return type / arguments
};
//Header2.h
class A; //Forward declaration of A
class B
{
public:
~B();
SmartPointer<A> Ptr;
};
//Header2.cpp
#include "Header2.h"
#include "Header1.h" // obtain full definiton of A
B::~B() = default; // destructor here knows full definition of A
The reason this works is because the destructor of SmartPointer is being called from code generated by compiler in destructor of B, but where ~B is defined depends on you. If you don't specify it at all, it will be generated in Header2.h, where the definition is missing, thus the warning. If you speficy it inside Header2.cpp and place the usual declaration in Header2.h, it will obviously work correctly in Header2.cpp because the full definition is known there. Other translation units such as Header1.cpp will work correctly too, because they will see the declaration of ~B and won't try to generate one on the spot. Instead, they'll just call the ~B by symbol, which will be resolved by linker later.
Under certain circumstances, you might need to move to the cpp also B's assignment operator(s) and/or, for some inexplicable reason, even constructor.
精彩评论