2009-06-05 5 views
7

Sto lavorando a un progetto in cui vengono conteggiati determinati oggetti conteggiati: è un'impostazione molto simile a COM. Ad ogni modo, il nostro progetto ha degli indicatori intelligenti che alleggeriscono la necessità di chiamare esplicitamente Add() e Release() per questi oggetti. Il problema è che a volte gli sviluppatori chiamano ancora Release() con il puntatore intelligente.I puntatori intelligenti possono nascondere o reindirizzare selettivamente le chiamate di funzione agli oggetti che stanno avvolgendo?

Quello che sto cercando è un modo per chiamare Release() dal puntatore intelligente per creare un errore in fase di compilazione o in fase di esecuzione. Il tempo di compilazione non mi sembra possibile. Pensavo di avere una soluzione run-time (vedi il codice sotto), ma non è abbastanza compilativa neanche. Apparentemente, la conversione implicita non è consentita dopo aver usato l'operatore ->().

In ogni caso, qualcuno può pensare a un modo per realizzare ciò che sto cercando di realizzare?

Mille grazie per il vostro aiuto!

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

Si prega di sostituire l'allocazione di stack "A a" con una nuova allocazione operatore? Questo non cambierà il problema originale, ma rimuoverà uno dei tipici errori brutti con l'utilizzo di puntatori intelligenti di conteggio dei riferimenti. – sharptooth

risposta

4

Non è possibile farlo - una volta sovraccaricato il operator -> si è bloccati - l'operatore sovraccarico si comporterà allo stesso modo senza coda di ciò che è giusto di esso.

È possibile dichiarare privati ​​i metodi Add() e Release() e rendere il puntatore intelligente un amico della classe di conteggio dei riferimenti.

+0

Questo è il mio obiettivo a lungo termine. Al momento però, abbiamo un sacco di codice legacy prima che venissero introdotti i puntatori intelligenti. Sfortunatamente, siamo di fronte a una scadenza incombente, e non posso implementare questa soluzione fino a qualche tempo nel futuro - si spera non in un futuro indefinito. – Kevin

3

operator-> deve restituire un puntatore o un oggetto che si sostiene operator->. Può essere ricorsivo. Ciò che non è possibile fare è che operator-> si comportino diversamente in base a ciò che appare sul lato destro dello ->.

Non riesco a pensare ad alcun approccio che non implichi in qualche modo la replica delle interfacce dei tuoi oggetti puntati, o ti obbliga a creare oggetti pubblicamente derivati ​​dal tuo puntare agli oggetti con Aggiungi e Rilasciati nascosti e resi privati ​​in la classe derivata e utilizzando un trucco Base* pBase = pDerived; pBase->Add(); per chiamare aggiungere e rilasciare dal puntatore intelligente.

0

ho preso a lavorare cambiando l'operatore sovraccarico in MySmartPtr e l'aggiunta di un operatore di sovraccarico 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; 
} 

uscita:

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

Qual è il punto di associazione di un puntatore di conteggio di riferimento a un oggetto allocato allo stack? – sharptooth

+0

non lo so, questo era il codice che ha postato sopra, ma risolto in modo che funzioni. – Lodle

+0

No, questo non funziona come speravo.Se pA-> Release() non è commentato, non c'è alcuna asserzione. L'idea era di "reindirizzare" la chiamata a Release(). – Kevin

0

vi suggerisco di utilizzare qualcosa come il seguente codice. Quello che vuoi non è possibile a meno che non sia tu voglia aggiungere un piccolo vincolo: gli oggetti devono essere copiabili-costruibili (e non ti dispiace usare questa possibilità). In questo caso, l'ereditarietà è un buon modo per andare.

#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

Quando si copia il puntatore intelligente, verrà copiato anche l'oggetto appuntito, che vanifica totalmente lo scopo di avere un puntatore intelligente conteggio di riferimento. –

+0

Hai ragione, ma la mia soluzione rimane valida, purché BOOST_STATIC_ASSERT venga utilizzato per generare errori in fase di compilazione. Lo scopo non è quello di generare codice effettivo utilizzabile, ma può essere utilizzato per rimuovere chiamate inutili a Add() e Release(). –