Могут ли интеллектуальные указатели выборочно скрывать или перенаправлять вызовы функций на объекты, которые они переносят? - PullRequest
7 голосов
/ 05 июня 2009

Я работаю над проектом, в котором сосчитаны определенные объекты - это очень похоже на настройку COM. В любом случае, у нашего проекта есть умные указатели, которые устраняют необходимость явного вызова Add () и Release () для этих объектов. Проблема в том, что иногда разработчики все еще вызывают Release () с умным указателем.

То, что я ищу, - это способ заставить функцию Release () из умного указателя создать ошибку во время компиляции или во время выполнения. Время компиляции не представляется возможным для меня. Я думал, что у меня есть решение во время выполнения (см. Код ниже), но оно не вполне компилируется. По-видимому, неявное преобразование не допускается после использования оператора -> ().

В любом случае, кто-нибудь может придумать способ достичь того, чего я пытаюсь достичь?

Большое спасибо за вашу помощь!

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;
}

Ответы [ 4 ]

4 голосов
/ 05 июня 2009

Вы не можете сделать это - после того, как вы перегрузили operator ->, вы застряли - перегруженный оператор будет вести себя одинаково независимо от того, что с ним связано.

Вы можете объявить методы Add () и Release () закрытыми и сделать умный указатель другом класса подсчета ссылок.

3 голосов
/ 05 июня 2009

operator-> должен возвращать указатель или объект, который сам поддерживает operator->. Это может быть рекурсивным. То, что вы не можете сделать, это заставить operator-> вести себя по-разному в зависимости от того, что появляется справа от ->.

Я не могу придумать ни одного подхода, который не предполагает каким-либо образом реплицировать интерфейсы ваших указательных объектов или требовать от вас создания объектов, публично полученных из ваших указанных объектов, с добавлением и отпусканием скрытых и сделанных закрытыми производный класс и использование Base* pBase = pDerived; pBase->Add(); трюка для вызова add и release из умного указателя.

0 голосов
/ 05 июня 2009

Я предлагаю вам использовать что-то вроде следующего кода. То, что вы хотите, невозможно , если только вы не захотите добавить небольшое ограничение: объекты должны быть копируемыми (и вы не против использовать эту возможность). В этом случае наследование - это хороший путь.

#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;
}
0 голосов
/ 05 июня 2009

Я заставил его работать, изменив перегруженный оператор в MySmartPtr и добавив оператор перегрузки в 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;
}

Выход:

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