2011-01-31 5 views
17

Con frecuencia me topo con el problema, que debo extender un constructor de copia generado por el compilador. Ejemplo:Cómo puedo extender un constructor de copia generado por el compilador

class xyz; 
class C 
{ 
    ... 
    int a, b, c; 
    std::set<int> mySet; 
    xyz *some_private_ptr; 
}; 

Supongamos, que some_private_ptr solamente se debe copiar en ciertas condiciones. Para otras condiciones, debe restablecerse al NULL en la copia. Así que tengo que escribir un constructor de copia como:

C::C(const C &other) : 
    a(other.a), 
    b(other.b), 
    c(other.c), 
    mySet(other.mySet) 
{  
    if(CanCopy(other.some_private_ptr)) // matches condition 
     some_private_ptr = other.some_private_ptr; 
    else 
     some_private_ptr = NULL; 
} 

El problema es que la clase podría tener un número de miembros de datos, y que muy probablemente se olvide de actualizar el constructor de copia cuando agrego un miembro de datos. Sería muy bueno si pudiera escribir.

C::C(const C &other) : 
    C::default_copy(other) 
{  
    if(CanCopy(other.some_private_ptr)) // matches condition 
     some_private_ptr = other.some_private_ptr; 
    else 
     some_private_ptr = NULL; 
} 

Esto haría que mi código sea más seguro y más fácil de mantener. Lamentablemente, no conozco esa posibilidad. ¿Hay alguna?

Respuesta

16

En el momento en que define su propia copiadora, el compilador no se molesta en generar una para usted. ¡Desafortunadamente esto significa que tienes que hacer todo el trabajo tú solo! Puede agrupar a los miembros en una clase de estructura impl_ dentro de su clase, y luego confiar en el copiador para eso.

por ejemplo:

class xyz; 
class C 
{ 
    struct impl_ 
    { 
    int a, b, c; 
    std::set<int> mySet; 
    xyz *some_private_ptr; 
    }; 

    impl_ data; 
}; 

ahora en su copia ctor

C::C(const C &other) : data(other.data) 
{ 
// specific stuff...  
} 
+0

+1 por esta buena idea ... viendo esto borré mi publicación: P – Nawaz

+0

@Fred, ¡gracias por la edición! Quise decir "copia por defecto", pero me perdí la * copia *, supongo que ahora es lo mismo ... – Nim

+0

Sabía lo que querías decir, pensé que era un simple error. :) "Predeterminado" es complicado porque "ctor predeterminado" tiene un significado específico, "copiador generado por compilador" o simplemente "generado por cctor" es menos ambiguo. –

18

La forma más sencilla es la introducción de una clase base:

class xyz; 

struct CDetail { 
    //... 
    int a, b, c; 
    std::set<int> mySet; 
    xyz *some_private_ptr; 
}; 

struct C : private CDetail { 
    C(C const &other) 
    : CDetail(other) 
    { 
    if (!CanCopy(other.some_private_ptr)) 
     some_private_ptr = 0; 
    // opposite case already handled 
    } 
}; 

Esto es un abuso de la herencia de En cierta medida, pero las ventajas sobre una clase "impl" anidada son 1) puede acceder a cada miembro como "nombre" en lugar de "dat" a.name"(código de reducción cambia al refactorizar), y 2) (aunque sólo se desea a veces) que puede 'promover' a miembros individuales protegida o pública without affecting other members:

struct C : private CDetail { 
protected: 
    using CDetail::a; 
}; 

struct D : C { 
    void f() { 
    cout << a; 
    } 
}; 

int main() { 
    D d; 
    d.f(); // D can access 'a' 
    cout << d.a; // main cannot access 'a' 
    return 0; 
} 
+0

+1 por esta buena idea ... al ver su idea y la de Nim, borré mi publicación: P – Nawaz

12

El problema aquí es que su clase está tratando de hacer demasiado. O bien use un recurso o administre un recurso. No haces las dos cosas, nunca, porque tu código se convertirá en un montón de slop inseguro. Y eso no está bien.

Debe diseñar una clase que administre un recurso que solo se copia bajo ciertas condiciones. Realmente no se ha expandido en lo que esas condiciones y por qué están allí en primer lugar (esa es una forma muy extraña de "copiar" datos, ¿está seguro de que esta es la mejor ruta?), Pero sería algo de esta manera:

// pointer to your condition member (not sure if this is even needed, 
// is this condition knowable via the pointer alone? you get the point) 
template <typename T, typename D, class Tag = void> 
class copy_conditional_ptr 
{ 
public: 
    copy_conditional_ptr(bool (D::*condition)(T*) const, T* value = 0) : 
    mCondition(condition), 
    mValue(value) 
    {} 

    // here's where the unique copy-semantics go 
    copy_conditional_ptr(const copy_conditional_ptr& other) : 
    mCondition(other.mCondition), 
    mValue(do_copy(other.mValue) ? other.mValue : 0) 
    {} 

    // other stuff for a smart pointer, 
    // copy-and-swap, etc... 

protected: 
    // protected because it's meant to be a base class 
    ~copy_conditional_ptr() 
    { 
     // whatever 
    } 

private: 
    bool do_copy(T* value) const 
    { 
     const D& self = static_cast<const D&>(*this); 
     return (self.*mCondition)(other.value); 
    } 

    bool (D::*mCondition)(T*) const; 
    T* mValue; 
}; 

continuación, se utiliza de esta manera:

class xyz; 

class C : private copy_conditional_ptr<xyz, C> 
{ 
public: 
    C() : 
    /* others, */ 
    copy_conditional_ptr(&C::CanCopy) 
    {} 

private: 
    int a, b, c; 
    std::set<int> mySet; 
}; 

y dejar que la gestión sea automático para el resto de la clase. La etiqueta es lo que puede tener múltiples en la misma clase:

class C : private copy_conditional_ptr<xyz, C, struct C_first>, 
      private copy_conditional_ptr<xyz, C, struct C_second> 
{ 
    // ... 
}; 
+5

+1, habría votado más si pudiera. esta respuesta va a las causas raíz en lugar de abordar solo los síntomas. –

+0

@Alf: gracias por el sentimiento. :) – GManNickG

+0

@GMan: para esto, debes definir la función 'CanCopy', ¿no? ... ¿también no entiendo cómo la gestión es automática ahora? ¿Puedes explicar un poco más? – Nawaz

0

diría crear un puntero inteligente que se encarga de la copia, y luego usarlo como un miembro de su clase. Estos códigos pueden darle una idea:

Dependiendo de cómo se inicie el constructor de la llamada base, los constructores del miembro se llamarán de la misma manera. Por ejemplo, vamos a empezar con:

struct ABC{ 
    int a; 
    ABC() : a(0) { printf("Default Constructor Called %d\n", a); }; 

    ABC(ABC & other) 
    { 
     a=other.a; 
     printf("Copy constructor Called %d \n" , a) ; 
    }; 
}; 

struct ABCDaddy{ 
    ABC abcchild; 
}; 

Usted puede hacer estas pruebas:

printf("\n\nTest two, where ABC is a member of another structure\n"); 
ABCDaddy aD; 
aD.abcchild.a=2; 

printf("\n Test: ABCDaddy bD=aD; \n"); 
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy cD(aD); \n"); 
ABCDaddy cD(aD); // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy eD; eD=aD; \n"); 
ABCDaddy eD; 
eD=aD;   // Does NOT call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is not called) 

Salida:

Default Constructor Called 0 

Test: ABCDaddy bD=aD; 
Copy constructor Called 2 

Test: ABCDaddy cD(aD); 
Copy constructor Called 2 

Test: ABCDaddy eD; eD=aD; 
Default Constructor Called 0 

disfrutar.

0

Usted puede colocar su miembro especial en una clase base, algo así como:

class xyz; 

class SpecialCopyXYZ 
{ 
public: 
    SpecialCopyXYZ() = default; 
    SpecialCopyXYZ(const SpecialCopyXYZ& rhs) 
    { 
     if (CanCopy(other.some_private_ptr)) { 
      some_private_ptr = other.some_private_ptr; 
     } else { 
      some_private_ptr = nullptr; 
     } 
    } 

    // SpecialCopyXYZ& operator=(const SpecialCopyXYZ& rhs) 

protected: 
    xyz *some_private_ptr = nullptr; 
}; 


class C : private SpecialCopyXYZ 
{ 
public: 
    C(const C &other) = default; 
private: 
    int a, b, c; 
    std::set<int> mySet; 
}; 

If SpecialCopyXYZ necesidad C datos, es posible utilizar CRTP y abatido.

Cuestiones relacionadas