2009-05-18 10 views
13

Necesito un objeto similar shared_ptr, pero que crea automáticamente un objeto real cuando intento acceder a sus miembros.¿Hay un puntero perezoso en C++?

Por ejemplo, tengo:

class Box 
{ 
public: 
    unsigned int width; 
    unsigned int height; 
    Box(): width(50), height(100){} 
}; 

std::vector< lazy<Box> > boxes; 
boxes.resize(100); 

// at this point boxes contain no any real Box object. 
// But when I try to access box number 50, for example, 
// it will be created. 

std::cout << boxes[49].width; 

// now vector contains one real box and 99 lazy boxes. 

¿Hay alguna aplicación, o debería escribir mi propia?

Respuesta

17

Es muy poco esfuerzo para tirar el tuyo.

template<typename T> 
class lazy { 
public: 
    lazy() : child(0) {} 
    ~lazy() { delete child; } 
    T &operator*() { 
     if (!child) child = new T; 
     return *child; 
    } 
    // might dereference NULL pointer if unset... 
    // but if this is const, what else can be done? 
    const T &operator*() const { return *child; } 
    T *operator->() { return &**this; } 
    const T *operator->() const { return &**this; } 
private: 
    T *child; 
}; 

// ... 

cout << boxes[49]->width; 
+2

tendrá sentido contener child como auto_ptr –

+0

Pero, ¿cómo inicializaría las cajas [49] -> width para tener un valor inicial no trivial (es decir, no 0)? Probablemente desee tener una interfaz que permita al constructor de * (cuadros [49]) recibir su índice como argumento, para que pueda distinguirse de los otros cuadros. Eso significa usar algo que no sea std: vector, y te pone en el dominio de vectores dispersos/matrices. –

+2

Incluso puede usar boost :: optional en lugar del puntero secundario. Usar boost :: opcional significa que se beneficia de su asignación de stack. No se usa ningún montón entonces –

2

Nunca he oído hablar de tal cosa, pero de nuevo hay muchas cosas de las que nunca he oído hablar. ¿Cómo podría el "puntero perezoso" poner datos útiles en las instancias de la clase subyacente?

¿Estás seguro de que un sparse matrix no es lo que realmente estás buscando?

+1

¿Por qué señalaste la matriz dispersa? –

+0

Porque una matriz dispersa llena una necesidad similar (aunque no idéntica). Tenga en cuenta que el ejemplo del póster muestra un vector de "indicadores vagos"; esto suena muy parecido a una matriz dispersa. –

0

Por lo que sé, no existe una implementación de este tipo de cosas. Sin embargo, no sería difícil crear uno.

10

Usando boost::optional, puede hacer que tal cosa:

// 100 lazy BigStuffs 
std::vector< boost::optional<BigStuff> > v(100); 
v[49] = some_big_stuff; 

construirá 100 de perezosos y asignar a uno de verdad some_big_stuffv[49]. boost::optional no usará la memoria del montón, pero utiliza la ubicación-nueva para crear objetos en un búfer asignado por la pila. Me gustaría crear una envoltura alrededor de boost::optional así:

template<typename T> 
struct LazyPtr { 
    T& operator*() { if(!opt) opt = T(); return *opt; } 
    T const& operator*() const { return *opt; } 

    T* operator->() { if(!opt) opt = T(); return &*opt; } 
    T const* operator->() const { return &*opt; }  
private: 
    boost::optional<T> opt; 
}; 

Esto ahora utiliza boost::optional para hacer telas. Se debe ayudar a la construcción en el lugar como éste (ejemplo de op*):

T& operator*() { if(!opt) opt = boost::in_place(); return *opt; } 

que no requeriría ninguna copia-ción. Sin embargo, el actual boost-manual no incluye esa sobrecarga del operador de asignación. La fuente lo hace, sin embargo. No estoy seguro de si esto es solo un defecto en el manual o si su documentación se omite intencionalmente. Así que usaría la forma más segura usando una asignación de copia usando T().

+2

'vector > v (100)' usará 100 * sizeof (Box), lo cual está bien, pero quizás OP no quiera usar memoria para las cajas que no están asignadas. Como OP no ha descrito más requisitos, no sabemos ... – ephemient

+0

Tiene razón, ese es un buen punto :) –

+0

Correcto, no quiero perder espacio en objetos no asignados. –