Click here to Skip to main content
15,893,814 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Lets say I have the following code structure in my program
C++
class Base
{
public:
Base(){//dosomething}
~Base(){//dosomething} //Note: Destructor is not virtual
...
virtual void function() {//dosomething}
};

class Derived: public Base
{
public:
Derived(){//dosomething}
~Derived(){//dosomething}
...
virtual void function() {//dosomething}
};


Now I write a code something like

C++
void somefunction()
{
std::shared_ptr<Base> stdBasePtr(new Derived() );

//do something with the Smart Pointers
}


Now after some function goes out of scope, since the destructor or Base is not virtual. I am expecting a memory leak.
Q1. Is my assumption correct?
Q2. If the answer to question above is no. Then how is it handled.
Q3. If the answer is yes. Then what can I code in my implementation of smart pointer to handle such scenarios.

Thanks in advance
Posted
Comments
pasztorpisti 26-Aug-13 13:51pm    
std::shared_ptr seems to handle this case. I've seen older shared_ptr implementations that didn't handle this well, probably because of the lack of proper support for template methods at the time...

shared_ptr helps you only in sharing ownership without putting anything into your class, it doesn't care about your inheritance. shared_ptr is non-intrusive and knows only about the Base class so it will delete your ptr only as a Base class pointer. Its your responsibility in this case to ensure correct deletion by putting a virtual destructor into your Base class. It would be impossible to ensure correct deletion the pointer class. Sorry, I wasn't right at this point, its possible to handle this with a template constructor that stores an additional deleter object inside your pointer (the deleter object has the virtual destructor instead of your class). Still, this is more error prone than using intrusive pointers most of the time.

Demonstrating the usage of a deleter object with a template ctor:
C++
class Deleter
{
public:
    virtual ~Deleter() {}
};

template <typename Deletable>
class TDeleter : public Deleter
{
public:
    TDeleter(Deletable* p)
        : m_Ptr(p)
    {}
    ~TDeleter()
    {
        delete m_Ptr;
    }
private:
    Deletable* m_Ptr;
};


template <typename T>
class Ptr
{
public:
    template <typename U>
    Ptr(U* p)
    {
        m_Deleter = new TDeleter<U>(p);
        m_Ptr = p;
    }
    ~Ptr()
    {
        delete m_Deleter;
    }
    T* operator->()                      { return m_Ptr; }
    const T* operator->() const          { return m_Ptr; }
    T& operator*()                      { return *m_Ptr; }
    const T& operator*() const          { return *m_Ptr; }
private:
    Ptr(const Ptr&);
private:
    Deleter* m_Deleter;
    T* m_Ptr;
};


class C
{
public:
    C() { printf("%s\n", __FUNCTION__); }
    ~C() { printf("%s\n", __FUNCTION__); }
    void BaseMethod() { printf("%s\n", __FUNCTION__); }
};

class D : public C
{
public:
    D() { printf("%s\n", __FUNCTION__); }
    ~D() { printf("%s\n", __FUNCTION__); }
};

void TestPtr()
{
    Ptr<C> p(new D);
    p->BaseMethod();
}


I like intrusive shared pointers much more. An intrusive shared pointer expects your class to handle its own refcounting by itself so an intrusive shared pointer expects your class to have its own AddRef/Release methods (or whatever you name them). So when an intrusive shared pointer takes ownership of your object then it calls its AddRef. When the pointer lets the object go aways then it calls its release method. For this to work the object either implements AddRef/Release itself, or usually instead of this you derive your object from a base class that already implements AddRef/Release along with refcounting and besides this it introduces a virtual destructor for you that automatically helps you to avoid destruction bugs. Such an intrusive shared pointer can easily be implemented so that it handles inheritance well, automatic upcast can work between such pointers without problems. Another nice feature can be in case of the base class you use in your objects that you can provide two implementations: one for single threaded use, and another one for multithreaded use that handles the reference count with atomic operations (__sync_add_and_fetch, InterlockedXXX, ...).

EDIT: I usually use only the following pointers in my own codebase:
- Intrusive shared pointer (single/multithreaded)
- Intrusive weak pointer
- auto pointer

Note that an object can be the target of both shared/weak pointers as long as all the shared/weak pointer references come from the same thread because using weak pointers in case of multithreading has no point.

EDIT: Both intrusive and nonintrusive pointers have their advantages. Nonintrusives are very useful when the referenced classes are not yours. Intrusive ones are less vulnerable to transferring "raw" c++ pointers from one reference into another. My experience is that the latter thing can cause very hard to find bugs and systems that work with intrusive ptrs are less buggy. This is of course just my observation/opinion.
 
Share this answer
 
v9
Q1: Yes and no. A memory leak might result out of this. But some compilers are smart enough to figure out how much memory was allocated in the first place and free the right amount. But consider that the destructor of Derived will not be called, which might have other negative effects like resource leaks, blocking locks, or again a secondary memory leak.

Q2: You were right about the potential memory leak.

Q3: The best solution is to make the destructor of your base class virtual. If you can't use a smart pointer to the correct class, in this case std::shared_ptr<Derived>.
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900