2008-12-16 11 views
5

Estoy implementando una biblioteca de matemáticas en C++. La biblioteca se compilará en una DLL para que quienes la utilicen solo necesiten los archivos de encabezado y las definiciones de las clases.Recuento de referencias en C++

Los usuarios de mis clases serán personas que sean nuevas en el idioma. Sin embargo, hay algunos objetos a los que se puede hacer referencia en varias partes de sus programas. Como no espero que hagan la gestión de la memoria, me gustaría hacerlo yo mismo. Por lo tanto, tengo que implementar el conteo de referencias (la recolección de basura no es una posibilidad).

Quiero hacer que la referencia a contar lo más transparente posible, por ejemplo ...

// Define a Bézier curve 
CVecList pts; 
pts.Add(Vector(0,0,0)); 
pts.Add(Vector(0,0,100)); 
pts.Add(Vector(0,100,0)); 
pts.Add(Vector(0,100,100)); 
CCurve* c1 = new CBezier(pts); 

// Define a 3rd order B-Spline curve 
pts.Clear(); 
pts.Add(Vector(0,0,0)); 
pts.Add(Vector(0,200,100)); 
pts.Add(Vector(0,200,200)); 
pts.Add(Vector(0,-200,100)); 
pts.Add(Vector(0,-200,200)); 
pts.Add(Vector(0,0,0)); 
CCurve* c2 = new CBSpline(pts,3); 

// The Bézier curve object must be deleted automatically 
// because the only reference to it has been released 
// Similar to IUnknown::Release() in COM 
c1 = c2; 

cosas se ponen un poco más difícil cuando defino objetos de superficie, debido a que algunas superficies se definen en términos de dos curvas:

CVecList pts; 
// ... 
CCurve* f = new CBezier(pts); 

pts.Clear(); 
// ... 
CCurve* g = new CBezier(pts); 

// Mixed surface: S(u,v) = (1-v)*f(u) + v*g(u) 
CSurface* s = new CMixed(f,g); 

// There are two references to the first Bézier curve, 
// the first one is f 
// the second one is hidden in a member of CMixed 

// Something similar applies to the second Bézier curve 

pensé que anulando operator = para los punteros podría haber ayudado:

// This is what I tried, but it's illegal: 
typedef CReferenceCounted* PRC; 
PRC& operator =(PRC& dest, PRC& source) 
{ 
    if (source) 
     source->AddRef(); 
    if (dest) 
     dest->Release(); 
    memcpy(&dest,&source,sizeof(PRC)); 
    return dest; 
} 

... pero luego descubrí que operator = no es válido a menos que sea un miembro no estático de una clase.

¿Alguien podría ayudarme?

+0

http://ootips.org/yonat/4dev/smart-pointers.html – derobert

+0

He intentado mantener mi plantilla de código libre, pero no puedo encontrar otra solución. Gracias. –

+0

Eduardo podría editar esta publicación, seleccionar el código en su pregunta, luego hacer clic en el botón con 0s y 1s? Esto aplicará resaltado de sintaxis al código. –

Respuesta

11

Lo que probó fue sobrecargar un operador para tipos escalares. C++ no le permite hacer eso excepto para las enumeraciones (además del punto en que operator = tiene que ser un miembro). Al menos uno de los tipos tiene que ser un tipo definido por el usuario. Por lo tanto, lo que desea hacer es envolver el puntero sin formato en una clase definida por el usuario, que sobrecarga el constructor, el constructor de copias, el operador de asignación de copias y el destructor y hace el recuento de referencias adecuado. Esta es una situación ideal para boost::shared_ptr, que hace exactamente eso:

boost::shared_ptr<CCurve> c1(new CBezier(pts)); 

El mismo trato con superficies:

CVecList pts; 
// ... 
boost::shared_ptr<CCurve> f(new CBezier(pts)); 

pts.Clear(); 
// ... 
boost::shared_ptr<CCurve> g(new CBezier(pts)); 

// Mixed surface: S(u,v) = (1-v)f(u) + vg(u) 
boost::shared_ptr<CSurface> s(new CMixed(f,g)); 

llevar alrededor de ese puntero inteligente, y se gestionará automáticamente el tiempo de vida de la apuntado al objeto: si la última copia del puntero sale del alcance, el objeto apuntado se libera. shared_ptr está diseñado para ser fácil de usar. Trata de evitar trabajar con punteros crudos tanto como puedas. Echar un vistazo a los punteros inteligentes, van a aliviar sus programadores viven con C++ :)

Edición: Si se va a envolver un shared_ptr, puede hacerlo utilizando el pimpl (empuñadura/cuerpo) modismo:

/* ---- wrapper in header file bezier.hpp */ 

struct CBezier { 
    CBezier(CVecList const& list); 
    void do_calc(); 
    // ... 

private: 
    struct CBezierImpl; 
    boost::shared_ptr<CBezierImpl> p; 
}; 

/* ---- implementation file bezier.cpp */ 

// private implementation 
struct CBezier::CBezierImpl { 
    CBezierImpl(CVecList const& list); 
    void do_calc(); 
    // ... 
}; 


CBezier::CBezier(CVecList const& list) 
:p(new CBezierImpl(list)) { 

} 

void CBezier::do_calc() { 
    // delegate to pimpl 
    p->do_calc(); 
} 

// ... 
+0

Me gustaría hacer algo como definir CCurve como CCurved_internal, luego typedef CCurve para impulsar :: shared_ptr ,,, – BubbaT

+0

He estado tratando de mantener mi plantilla de código libre, pero no puedo encontrar otra solución. Gracias. –

+0

podrías, si envuelves, usar CCurve como no puntero y asignar el recurso dentro de él. y luego CCurve podría hacer el recuento de referencias, eliminando el puntero administrado cuando sea necesario. simplemente podría envolver un shared_ptr y hacerlo transparente para sus alumnos. –

0

me gustaría recomendar intrusive_ptr en lugar de shared_ptr para los objetos que se pueden controlar para un mejor rendimiento y facilidad de uso, como se puede asignar un puntero prima para intrusive_ptr más tarde, debido a que la cuenta de referencia está incrustado en el objeto.

1

Si está diseñando una biblioteca matemática, pase mucho tiempo pensando si sus clases pueden parecerse a int o std :: complex. Es decir, que los valores se comporten como valores. P.ej.

std::vector<math::point3d> pts; 
pts.push_back(math::point3d(0,0,0)); 
pts.push_back(math::point3d(110,0,0)); 
pts.push_back(math::point3d(0,100,0)); 
pts.push_back(math::point3d(0,0,100)); 
CCurve c1 = make_bezier(pts); 
0

Los usuarios de mis clases serán las personas que son nuevos en el lenguaje.

¿Está su clase diseñada para un curso de programación?

Si este es el caso, evitaría el uso de punteros y utilizar sólo copiar constructores/asignación:

  • Rendimiento/La memoria no es una prioridad
  • Hacer la gestión de memoria sí mismo le mostrará un muy mal ejemplo sobre cómo usar nuevo/eliminar
  • Usar cualquier tipo de puntero inteligente sin conocer la administración de la memoria podría causar mucha confusión más adelante.
0

Estoy de acuerdo con Guishu y MSalters. Incluso si no es para un curso de programación, puede ser agradable imitar el aspecto matemático más de cerca (por ejemplo, vector3 = vector1 + vector2, etc.).

Lo que también se puede hacer es usar copiar-en-escribir (el reenfoque es una consecuencia lógica), pero solo internamente. Eso puede darle asignaciones lo suficientemente rápidas, eliminar la administración de heap en el lado del cliente y la similitud con la notación matemática.

Tenga en cuenta, sin embargo, que hay librerías matemáticas disponibles para C++ (TNT, fuera de lo común). ¿Consideras basar tu trabajo en eso?

Cuestiones relacionadas