2009-06-05 10 views
7

Estoy trabajando en un proyecto donde se hace referencia a ciertos objetos; es una configuración muy similar a COM. De todos modos, nuestro proyecto tiene punteros inteligentes que alivian la necesidad de llamar explícitamente a Add() y Release() para estos objetos. El problema es que a veces los desarrolladores aún llaman a Release() con el puntero inteligente.¿Los punteros inteligentes pueden ocultar o redirigir selectivamente las llamadas de función a los objetos que están envolviendo?

Lo que estoy buscando es una forma de llamar al Release() desde el puntero inteligente para crear un error en tiempo de compilación o en tiempo de ejecución. El tiempo de compilación no me parece posible. Pensé que tenía una solución en tiempo de ejecución (ver el código a continuación), pero tampoco compila del todo. Aparentemente, la conversión implícita no está permitida después de usar operator ->().

De todos modos, ¿alguien puede pensar en una forma de lograr lo que estoy tratando de lograr?

Muchas gracias por su ayuda!

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

¿Puede reemplazar la asignación de la pila "A a" con una nueva asignación del operador? Esto no cambiará el problema original, pero eliminará uno de los típicos errores desagradables con el uso de punteros inteligentes de recuento de referencias. – sharptooth

Respuesta

4

No puede hacerlo; una vez que ha sobrecargado el operator -> está atascado, el operador sobrecargado se comportará de la misma manera sin tener en cuenta lo que está a la derecha de él.

Puede declarar los métodos Add() y Release() privados y hacer que el puntero inteligente sea un amigo de la clase de recuento de referencias.

+0

Este es mi objetivo a largo plazo. Ahora mismo, tenemos mucho código heredado antes de que se introdujeran los punteros inteligentes. Lamentablemente, nos enfrentamos a una fecha límite inminente y no puedo implementar esta solución hasta algún momento en el futuro, es de esperar que no tenga un futuro indefinido. – Kevin

3

operator-> tiene que devolver un puntero o un objeto que a su vez soporta operator->. Puede ser recursivo. Lo que no puede hacer es tener operator-> comportarse de manera diferente en función de lo que aparece en el lado derecho de ->.

No puedo pensar en ningún enfoque que no implique de alguna manera replicar las interfaces de sus objetos apuntados, o que necesite crear objetos derivados públicamente de los objetos apuntados con Agregar y liberar ocultos y hechos privados en la clase derivada y el uso de un truco Base* pBase = pDerived; pBase->Add(); para llamar a agregar y liberar desde el puntero inteligente.

0

lo tengo para trabajar cambiando el operador sobrecargado en MySmartPtr y la adición de un operador de sobrecarga en 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; 
} 

Salida:

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

¿Cuál es el punto de vincular un puntero de recuento de referencias a un objeto asignado a la pila? – sharptooth

+0

no lo sé, este fue el código que publicó anteriormente pero lo corrigió para que funcione. – Lodle

+0

No, esto no funciona como esperaba.Si pA-> Release() no está comentado, no hay aserción. La idea era "redirigir" la llamada a Release(). – Kevin

0

le sugiero que utilice algo como el siguiente código. Lo que desea no es posible a menos que esté dispuesto a agregar una pequeña restricción: los objetos deben ser copiables (y no le molesta usar esta posibilidad). En este caso, la herencia es un buen camino por recorrer.

#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

Cuando copie el puntero inteligente, el objeto puntiagudo también se copiará, lo que frustra por completo el propósito de tener un puntero inteligente de recuento de referencia. –

+0

Tiene razón, pero mi solución sigue en pie, siempre que BOOST_STATIC_ASSERT se use para generar errores en tiempo de compilación. El propósito no será generar código utilizable real, sino que se puede usar para eliminar llamadas inútiles a Agregar() y Liberar(). –

Cuestiones relacionadas