2010-01-17 16 views
37

Tengo algunos contenedores en una clase, por ejemplo, vector o mapa que contienen shared_ptr's a objetos que viven en el montón.C++ puntero inteligente const correctness

Por ejemplo

template <typename T> 
class MyExample 
{ 
public: 

private: 
vector<tr1::shared_ptr<T> > vec; 
map<tr1::shared_ptr<T> , int> h; 
}; 

Quiero tener una interfaz pública de esta clase que devuelve a veces shared_ptrs a const objetos (a través de shared_ptr<const T>) y, a veces shared_ptr<T> donde que permiten la persona que llama para mutar los objetos. Quiero corrección lógica const, por lo que si marcó un método como const, no puede cambiar los objetos en el montón.

Preguntas:

1) Estoy confundido por la intercambiabilidad de tr1::shared_ptr<const T> y tr1::shared_ptr<T>. Cuando alguien pasa un shared_ptr shared_ptr<const T> en la clase, puedo almacenar como un shared_ptr<T> o shared_ptr<const T> dentro del vector y correspondencia o puedo cambiar el mapa, tipos de vectores (por ejemplo insert_elemeent (shared_ptr<const T> obj)?

2) ¿Es mejor instanciar las clases de la siguiente manera: MyExample<const int>? Eso parece indebidamente restrictivo, porque nunca puedo devolver un shared_ptr<int>?

Respuesta

3

una cosa es darse cuenta de que:

tr1::shared_ptr<const T> imita la funcionalidad de T const * saber lo que apunta es constante, pero el puntero en sí no lo es.

Para que pueda asignar un nuevo valor a su puntero compartido, pero esperaría que no pudiera usar el shared_ptr desreferenciado como un valor l.

+1

"l-value." ¡No es necesario asignar un valor l! – curiousguy

5

Si alguien le pasa un shared_ptr<const T>, nunca debería poder modificar T. Es, por supuesto, técnicamente posible emitir el const T a solo T, pero eso rompe la intención de hacer el Tconst. Por lo tanto, si desea que las personas puedan agregar objetos a su clase, deberían proporcionarle shared_ptr<T> y no shared_ptr<const T>. Cuando devuelve cosas de su clase, no desea modificarlas, es decir, cuando usa shared_ptr<const T>.

shared_ptr<T> se puede convertir automáticamente (sin un molde explícito) a un shared_ptr<const T>, pero no al revés. Puede ayudarte (y deberías hacerlo de todos modos) a hacer un uso liberal de los métodos const. Cuando defina un método de clase const, el compilador no le permitirá modificar ninguno de sus miembros de datos ni devolver nada excepto un const T. Por lo tanto, usar estos métodos te ayudará a asegurarte de que no hayas olvidado algo, y ayudará a los usuarios de tu clase a comprender cuál es la intención del método. (Ejemplo: virtual shared_ptr<const T> myGetSharedPtr(int index) const;)

Estás en lo correcto en su segunda declaración, es probable que no desea crear una instancia de su clase como <const T>, ya que nunca será capaz de modificar cualquiera de sus T s.

34

shared_ptr<T> y shared_ptr<const T> son no intercambiable. Va en una dirección: shared_ptr<T> es convertible a shared_ptr<const T> pero no al revés.

Observe:

// f.cpp 

#include <memory> 

int main() 
{ 
    using namespace std; 

    shared_ptr<int> pint(new int(4)); // normal shared_ptr 
    shared_ptr<const int> pcint = pint; // shared_ptr<const T> from shared_ptr<T> 
    shared_ptr<int> pint2 = pcint; // error! comment out to compile 
} 

compilar a través

cl/EHsc f.cpp

También puede sobrecargar una función basada en un constness. Puedes combinar estos dos hechos para hacer lo que quieras.

En cuanto a su segunda pregunta, MyExample<int> probablemente tenga más sentido que MyExample<const int>.

11

que sugeriría la siguiente methotology:

template <typename T> 
class MyExample 
{ 
    private: 
    vector<shared_ptr<T> > data; 

    public: 
    shared_ptr<const T> get(int idx) const 
    { 
     return data[idx]; 
    } 
    shared_ptr<T> get(int idx) 
    { 
     return data[idx]; 
    } 
    void add(shared_ptr<T> value) 
    { 
     data.push_back(value); 
    } 
}; 

Esto asegura const-corrección. Como ve, el método add() no usa < const T> pero < T> porque tiene la intención de que la clase almacene Ts no const Ts. Pero al acceder const, devuelve < const T> que no es un problema ya que shared_ptr < T> se puede convertir fácilmente a shared_ptr < const T>. Y si los dos métodos get() devuelven copias de los shared_ptr en su almacenamiento interno, la persona que llama no puede cambiar accidentalmente el objeto al que apuntan sus punteros internos. Todo esto es comparable a la variante de puntero no inteligente:

template <typename T> 
class MyExamplePtr 
{ 
    private: 
    vector<T *> data; 

    public: 
    const T *get(int idx) const 
    { 
     return data[idx]; 
    } 
    T *get(int idx) 
    { 
     return data[idx]; 
    } 
    void add(T *value) 
    { 
     data.push_back(value); 
    } 
}; 
+0

¿No debería ser que 'shared_ptr ' se puede convertir fácilmente en shared_ptr 'y no al revés? – user231536

+0

Al regresar solo un miembro simple, la sobrecarga parece no ser un gran problema. Pero, ¿qué sucede si devuelve un 'shared_ptr' de un' vector', donde tiene que calcular cuál es el correcto. ¿Cómo se evita la duplicación de código? – thomthom

Cuestiones relacionadas