Question

I'm working on a project where certain objects are referenced counted -- it's a very similar setup to COM. Anyway, our project does have smart pointers that alleviate the need to explicitly call Add() and Release() for these objects. The problem is that sometimes, developers are still calling Release() with the smart pointer.

What I'm looking for is a way to have calling Release() from the smart pointer create a compile-time or run-time error. Compile-time doesn't seem possible to me. I thought I had a run-time solution (see code below), but it doesn't quite compile either. Apparently, implicit conversion isn't allowed after using operator->().

Anyway, can anyone think of a way to accomplish what I'm trying to accomplish?

Many thanks for your help!

Kevin

#include <iostream>
#include <cassert>

using namespace std;

class A
{
public:
    void Add()
    {
        cout << "A::Add" << endl;
    }

    void Release()
    {
        cout << "A::Release" << endl;
    }

    void Foo()
    {
        cout << "A::Foo" << endl;
    }
};

template <class T>
class MySmartPtrHelper
{
    T* m_t;

public:

    MySmartPtrHelper(T* _t)
        : m_t(_t)
    {
        m_t->Add(); 
    }

    ~MySmartPtrHelper()
    {
        m_t->Release(); 
    }

    operator T&()
    {
        return *m_t;
    }

    void Add()
    {
        cout << "MySmartPtrHelper::Add()" << endl;
        assert(false);
    }

    void Release()
    {
        cout << "MySmartPtrHelper::Release()" << endl;
        assert(false);
    }
};

template <class T>
class MySmartPtr
{
    MySmartPtrHelper<T> m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(_pT)
    {
    }

    MySmartPtrHelper<T>* operator->()
    {
        return &m_helper;
    }
};

int main()
{
    A a;

    MySmartPtr<A> pA(&a);

    pA->Foo(); // this currently fails to compile.  The compiler
               // complains that MySmartPtrHelper::Foo() doesn't exist.

    //pA->Release(); // this will correctly assert if uncommented.

    return 0;
}
Was it helpful?

Solution

You can't do it - once you've overloaded the operator -> you're stuck - the overloaded operator will behave the same way reardless of what is rightwards of it.

You could declare the Add() and Release() methods private and make the smart pointer a friend of the reference-counting class.

OTHER TIPS

operator-> has to return a pointer or an object which itself supports operator->. It can be recursive. What you can't do is to have operator-> behave differently based on what appears on the right hand side of the ->.

I can't think of any approach that doesn't involve somehow replicating the interfaces of your pointed-to objects, or require you to create objects publicly derived from your pointed to objects with Add and Release hidden and made private in the derived class and using a Base* pBase = pDerived; pBase->Add(); trick to call add and release from the smart pointer.

i got it to work by changing the overloaded operator in MySmartPtr and adding an overload operator in MySmartPtrHelper:

#include <iostream>
#include <cassert>

using namespace std;

class A
{
public:
    void Add()
    {
        cout << "A::Add" << endl;
    }

    void Release()
    {
        cout << "A::Release" << endl;
    }

    void Foo()
    {
        cout << "A::Foo" << endl;
    }
};

template <class T>
class MySmartPtrHelper
{
    T* m_t;

public:

    MySmartPtrHelper(T* _t)
        : m_t(_t)
    {
        m_t->Add(); 
    }

    ~MySmartPtrHelper()
    {
        m_t->Release(); 
    }

    operator T&()
    {
        return *m_t;
    }

    T* operator->()
    {
        return m_t;
    }


    void Add()
    {
        cout << "MySmartPtrHelper::Add()" << endl;
        assert(false);
    }

    void Release()
    {
        cout << "MySmartPtrHelper::Release()" << endl;
        assert(false);
    }
};

template <class T>
class MySmartPtr
{
    MySmartPtrHelper<T> m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(_pT)
    {
    }

    T* operator->()
    {
        return m_helper.operator->();
    }
};

int main()
{
    A a;

    MySmartPtr<A> pA(&a);

    pA->Foo(); 
    //pA->Release(); // this will correctly assert if uncommented.

    return 0;
}

Output:

macbook-2:~ $ ./a.out 
A::Add
A::Foo
A::Release

I suggest you use something like the following code. What you want is not possible unless you are willing to add a small constraint : objects must be copy-constructible (and you don't mind using this possibility). In this case, inheritance is a good way to go.

#include <iostream>
#include <cassert>

using namespace std;

template <class T>
class MySmartPtrHelper : public T
{

public:

    MySmartPtrHelper(T* _t)
        : m_t(*_t)
    {
        delete _t;
        ((T*) this)->Add();
    }

    ~MySmartPtrHelper()
    {
        ((T*) this)->Release(); 
    }

    void Add()
    {
        cout << "MySmartPtrHelper::Add()" << endl;
        //will yield a compile-time error  
        BOOST_STATIC_ASSERT(false) 
    }

    void Release()
    {
        cout << "MySmartPtrHelper::Release()" << endl;
        //will yield a compile-time error  
        BOOST_STATIC_ASSERT(false) 
    }
};

template <class T>
class MySmartPtr
{
   MySmartPtrHelper<T>* m_helper;
   // Uncomment if you want to use boost to manage memory
   // boost::shared_ptr<MySmartPtrHelper<T> > m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(new MySmartPtrHelper<T>(_pT))
    {
    }

    MySmartPtrHelper<T>* operator->()
    {
        return m_helper;
    }
};

int main()
{
    MySmartPtr<A> pA(new A());

    pA->Foo();

    //pA->Release(); // this will correctly assert if uncommented.

    return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top