2011-05-09 23 views
5

Tengo una clase de contenedor simple de bajo nivel que es utilizada por una clase de archivo de más alto nivel. Básicamente, la clase de archivo utiliza el contenedor para almacenar modificaciones localmente antes de guardar una versión final en un archivo real. Algunos de los métodos, por lo tanto, se transfieren directamente de la clase contenedora a la clase de archivo. (Por ejemplo, Resize().)Métodos de copia del miembro

Acabo de definir los métodos en la clase de archivo para llamar a sus variantes de clase de contenedor. Por ejemplo:

void FileClass::Foo() 
{ 
    ContainerMember.Foo(); 
} 

Esto, sin embargo, es cada vez más una molestia. ¿Hay una mejor manera de hacer esto?

Aquí está un ejemplo simplificado:

class MyContainer 
{ 
    // ... 

    public: 

    void Foo() 
    { 
     // This function directly handles the object's 
     // member variables. 
    } 
} 

class MyClass 
{ 
    MyContainer Member; 

    public: 

    void Foo() 
    { 
     Member.Foo(); 

     // This seems to be pointless re-implementation, and it's 
     // inconvenient to keep MyContainer's methods and MyClass's 
     // wrappers for those methods synchronized. 
    } 
} 
+0

Veo la composición aquí, no la herencia. ¿Me estoy perdiendo de algo? –

+0

@Doug Estoy preguntando si hay una forma de heredar de la clase contenedor, o 'MyContainer' en mi ejemplo, por solo unos pocos métodos. (O una mejor solución, si hay una). Editaré la pregunta para que sea más clara. – Maxpm

+1

En ese caso, parece que debes dividir MyContainer en una clase que tenga los métodos que deseas (y luego heredar de eso) y el resto de los métodos de MyContainer en una clase diferente. – dlev

Respuesta

7

Bueno, ¿por qué no heredar de manera privada desde MyContainer y exponer las funciones que desea reenviar con una declaración using? Eso se llama "La implementación de MyClassen términos deMyContainer.

class MyContainer 
{ 
public: 
    void Foo() 
    { 
     // This function directly handles the object's 
     // member variables. 
    } 

    void Bar(){ 
     // ... 
    } 
} 

class MyClass : private MyContainer 
{ 
public: 
    using MyContainer::Foo; 

    // would hide MyContainer::Bar 
    void Bar(){ 
     // ... 
     MyContainer::Bar(); 
     // ... 
    } 
} 

Ahora el 'exterior' será capaz de llamar directamente Foo, mientras Bar sólo se puede acceder en el interior de MyClass. Si ahora se hace una función con el mismo nombre, se esconde la función de base y que puede envolver funciones de base por el estilo. Por supuesto, ahora tiene que calificar totalmente la llamada a la función de base, o tendrá que entrar en una recursión infinita.


Además, yo inheritence f desea permitir subclases (no polymorphical) de MyClass, que este es uno de los pocos lugares, fueron protegidas es realmente útil:

class MyClass : protected MyContainer{ 
    // all stays the same, subclasses are also allowed to call the MyContainer functions 
}; 

no polymorphical si su MyClass no tiene destructor virtual.

1

Sí, el mantenimiento de una clase de proxy como esto es muy molesto. Su IDE puede tener algunas herramientas para hacerlo un poco más fácil. O quizás pueda descargar un complemento IDE.

Pero generalmente no es muy difícil a menos que necesite admitir docenas de funciones y modificaciones y plantillas.

suelo escribir como ellos:

void Foo()  { return Member.Foo(); } 
int Bar(int x) { return Member.Bar(x); } 

Es agradable y simétrica. C++ le permite devolver valores vacíos en funciones anuladas porque eso hace que las plantillas funcionen mejor. Pero puedes usar lo mismo para hacer que el otro código sea más bonito.

1

Considere lo que tiene sentido en su caso: composición (tiene una) o herencia (es una) relación entre MyClass y MyContainer.

Si no desea tener este tipo de código más, está prácticamente restringido a la herencia de implementación (MyContainer como base/clase base abstracta). Sin embargo, debe asegurarse de que esto tenga sentido en su aplicación, y no hereda exclusivamente para la implementación (la herencia para la implementación es mala).

En caso de duda, lo que tiene probablemente esté bien.

EDITAR: Estoy más acostumbrado a pensar en Java/C# y pasé por alto el hecho de que C++ tiene la mayor flexibilidad de herencia que Xeo utiliza en su respuesta. Eso simplemente se siente como una buena solución en este caso.

0

Esta característica que necesita para escribir grandes cantidades de código es en realidad una característica necesaria. C++ es un lenguaje detallado, y si intentas evitar escribir código con C++, tu diseño nunca será muy bueno.

Pero el verdadero problema con esta pregunta es que la clase no tiene ningún comportamiento. Es solo un envoltorio que no hace nada. Cada clase necesita hacer algo más que pasar datos.

La clave es que cada clase tiene una interfaz correcta. Este requisito hace que sea necesario escribir funciones de reenvío. El objetivo principal de cada función miembro es distribuir el trabajo requerido a todos los miembros de datos. Si solo tiene un miembro de datos y aún no ha decidido qué se supone que debe hacer la clase, todo lo que tiene es funciones de reenvío. Una vez que agregue más objetos miembro y decida qué debe hacer la clase, sus funciones de remisión cambiarán a algo más razonable.

Una cosa que ayudará con esto es mantener sus clases pequeñas. Si la interfaz es pequeña, cada clase proxy solo tendrá una interfaz pequeña y la interfaz no cambiará muy a menudo.

+0

La clase * no * hace algo. Mi ejemplo fue simplificado por el bien de la pregunta. – Maxpm

Cuestiones relacionadas