2009-06-05 9 views
7

Pracuję nad projektem, do którego odwoływano się z określonymi obiektami - to bardzo podobne ustawienie do COM. W każdym razie nasz projekt ma inteligentne wskaźniki, które zmniejszają potrzebę jawnego wywoływania funkcji Add() i Release() dla tych obiektów. Problem polega na tym, że czasami deweloperzy wciąż wywołują funkcję Release() za pomocą inteligentnego wskaźnika.Czy inteligentne wskaźniki mogą selektywnie ukrywać lub przekierowywać wywołania funkcji do obiektów, które owijają?

To, czego szukam, to metoda wywoływania funkcji Release() z inteligentnego wskaźnika, powodująca błąd kompilacji lub wykonywania. Czas kompilacji nie wydaje mi się możliwy. Myślałem, że mam rozwiązanie czasu pracy (zobacz kod poniżej), ale nie całkiem się kompiluje. Najwyraźniej niejawna konwersja nie jest dozwolona po użyciu operatora ->().

W każdym razie, czy ktoś może pomyśleć o sposobie osiągnięcia tego, co próbuję osiągnąć?

Dziękujemy za pomoc!

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

Czy zastąpicie przydział A "przy użyciu nowego przydziału operatora? Nie spowoduje to zmiany pierwotnego problemu, ale usunie jeden z typowych nieprzyjemnych błędów za pomocą inteligentnych wskaźników zliczających odniesienia. – sharptooth

Odpowiedz

4

Nie możesz tego zrobić - po przeciążeniu operator -> utkniesz - przeciążony operator będzie zachowywał się w ten sam sposób, bez względu na to, co znajduje się w jego prawej części.

Możesz zadeklarować metody Add() i Release() jako prywatne i uczynić inteligentny wskaźnik przyjacielem klasy zliczającej referencje.

+0

To jest mój długoterminowy cel. Teraz jednak mamy dużo starszego kodu przed wprowadzeniem inteligentnych wskaźników. Niestety mamy do czynienia z zbliżającym się terminem i nie mogę wdrożyć tego rozwiązania do pewnego czasu w przyszłości - mam nadzieję, że nie jest to pewna nieokreślona przyszłość. – Kevin

3

operator-> musi powrócić wskaźnik lub obiektu, który sam wspiera operator->. Może być rekurencyjny. To, czego nie możesz zrobić, to mieć różne zachowanie w zależności od tego, co pojawia się po prawej stronie ->.

Nie mogę wymyślić żadnego podejścia, które nie wymaga w jakiś sposób replikowania interfejsów wskazanych obiektów, ani nie wymaga tworzenia obiektów publicznie wyprowadzonych z wskazanych obiektów z ukrytymi i ukrytymi dodatkami i prywatnymi. klasa pochodna i za pomocą sztuczki Base* pBase = pDerived; pBase->Add(); wywoływać dodawanie i zwalnianie z inteligentnego wskaźnika.

0

mam go do pracy przez zmianę przeciążony operator w MySmartPtr i dodanie operatora przeciążenia w 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; 
} 

wyjściowa:

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

Jaki jest sens powiązania wskaźnika zliczania referencji z obiektem przydzielonym do stosu? – sharptooth

+0

Nie wiem, to kod, który napisałem powyżej, ale naprawiony, więc działa. – Lodle

+0

Nie, to nie działa tak jak się spodziewałem.Jeśli pA-> Release() zostanie odkomentowany, nie ma żadnego potwierdzenia. Chodziło o "przekierowanie" połączenia do Release(). – Kevin

0

Proponuję użyć coś podobnego do następującego kodu. Czego chcesz, nie jest możliwe chyba że jesteś skłonny dodać małe ograniczenie: obiekty muszą być możliwe do skopiowania (i nie masz nic przeciwko użyciu tej możliwości). W takim przypadku dziedziczenie to dobry sposób na odejście.

#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

Po skopiowaniu inteligentnego wskaźnika, ostro zakończony obiekt również zostanie skopiowany, co całkowicie uniemożliwia uzyskanie odniesienia liczącego inteligentny wskaźnik. –

+0

Masz rację, ale moje rozwiązanie wciąż trwa, dopóki BOOST_STATIC_ASSERT jest używany do generowania błędów podczas kompilacji. Celem nie jest wygenerowanie rzeczywistego użytecznego kodu, ale można go użyć do usunięcia niepotrzebnych wywołań funkcji Add() i Release(). –

Powiązane problemy