2009-10-25 28 views
7

¿Hay una manera de añadir nuevos métodos a una clase, sin modificar la definición original de la clase (es decir, compilado lib contiene la clase y el correspondiente fichero .h) al igual que los métodos de extensión de clase C# 's?Extensión Clase C++

+1

Para referencia futura, esto se llama 'Monkey patching'. – LiraNuna

Respuesta

15

No. C++ no tiene esa capacidad.

Como se ha mencionado en otras respuestas, las soluciones comunes son:

  • definir una clase derivada, tal vez con una fábrica de ocultar la clase de implementación real
  • Definir una clase decorator
  • definir funciones que operan en instancias de la clase
+0

También se puede hacer por [envío doble] (http://en.wikipedia.org/wiki/Double_dispatch) – Bossliaw

11

No, no puede hacer esto en C++.

Si usted quiere lograr algo como esto tiene 2 opciones,

  • Se puede heredar de la clase (si esto es una opción, puede que no sea legal que la clase no puede haber sido escrito a permitir que la herencia)
  • usted puede escribir su propia clase de contenedor que tiene la misma interfaz + nuevos métodos y delegado a la que desea extender.

Yo prefiero el enfoque de delegación.

+4

+1 por "favor composición sobre herencia" – Rob

+0

Buena respuesta, pero no estoy seguro de que alguien haga preguntas como estos saben qué implementar cuando escucha términos como "heredar" o "delegar". –

+1

@mh, probablemente no. pero ahora saben los términos correctos para poner en una búsqueda SO o Google – Glen

-2

Lo siento, no. Una vez que su código está en obj, no puede cambiarlo. Si esto se puede hacer en VC, las clases parciales ya tendrían soporte. Sin embargo, hay una excepción, los métodos del operador se pueden extender usando funciones globales, muy parecido a cómo se implementa cout < < en STL.

5

métodos de extensión C# clase son en su mayoría de azúcar sintáctica. Obtiene la misma funcionalidad con funciones gratuitas (es decir, funciones con referencia o referencia constante a su clase como primer parámetro). Dado que esto funciona bien para el STL, ¿por qué no para su clase?

-3

Claro que puedes:


template <typename Ext> 
class Class: public Ext { /* ... */ }; 

Eso no quiere decir que sea el mejor enfoque sin embargo.

1

Generalmente no. Sin embargo, si la biblioteca no crea instancias de la clase que requieren su extensión y que son capaces de modificar todas las secciones de la aplicación que crean una instancia de la clase y requieren extensiones, hay una manera usted puede ir:

  • Crea una función de fábrica que se llama en todos los lugares que requieren una instancia de la clase y devuelve un puntero a la instancia (google for Design Patterns Factory, ...).
  • Crea una clase derivada con las extensiones que quieras.
  • Haga que la función de fábrica devuelva su clase derivada en lugar de la clase original.

Ejemplo:


    class derivedClass: public originalClass { /* ... */}; 

    originalClass* createOriginalClassInstance() 
    { 
     return new derivedClass(); 
    } 
  • Siempre que necesite acceder a las extensiones, es necesario emitir el reparto original de la clase derivada, por supuesto.

Esto es más o menos cómo implementar el método "heredar" sugerido por Glen. El método de la "clase contenedora con la misma interfaz" de Glen también es muy agradable desde un punto de vista teórico, pero tiene propiedades ligeramente diferentes que hacen que sea menos probable que funcione en su caso.

1

Hay una manera en que se puede hacer. Y eso es relajando un poco tus requisitos. En C++, la gente a menudo dice que la interfaz de una clase no consiste solo de sus funciones miembro, sino de todas las funciones que funcionan en la clase.

Es decir, las funciones no miembro a las que se puede asignar la clase como parámetro se deben considerar parte de su interfaz.

Por ejemplo, std::find() o std::sort() son parte de la interfaz de std::vector, aunque no son miembros de la clase.

Y si acepta esta definición, entonces siempre puede extender una clase simplemente agregando funciones que no sean miembros.

0

No puede agregar métodos o datos físicamente al archivo de clase que está en forma binaria. Sin embargo, puede agregar métodos y datos (funcionalidad y estado) a los objetos de esa clase escribiendo clases de extensión. Esto no es sencillo y requiere una programación basada en Meta-Object-Protocol y Interface. Debe hacer mucho para lograr esto en C++, ya que no es compatible con Reflection de fábrica. En dicha implementación cuando consulta la interfaz implementada por su nueva clase de extensión a través del puntero de objeto de clase original, la implementación del metaobjeto devuelve ese puntero de interfaz a través del objeto metaclase para la clase de extensión que crea en tiempo de ejecución. Esta es la cantidad de marcos de aplicaciones de software personalizables (basados ​​en complementos) que funcionan. Sin embargo, debe recordar que se deben escribir muchos otros mecanismos MOP para instanciar metaobjetos para todas las clases utilizando diccionarios en los que se describen las relaciones de objeto y dar los punteros de interfaz correctos para los objetos de clase originales y extendidos. CATIA V5 de Dassault Systemes está escrito en una arquitectura de este tipo llamada CAA V5, donde puede ampliar los componentes existentes escribiendo nuevas clases de extensión con la funcionalidad deseada.

3

En C++ puede usar funciones gratuitas, pero a veces los métodos de extensión funcionan mejor cuando anida muchas funciones juntas. Echar un vistazo a este código C#:

var r = numbers.Where(x => x > 2).Select(x => x * x); 

Si tenemos que escribir esto en C++ usando la función gratuita que se vería así:

auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; }); 

Esto no sólo es difícil de leer, pero es difícil escribir. La forma común de resolver esto es crear lo que se llama una función pipable. Estas funciones se crean al sobrecargar el operador de tubería | (que en realidad es el operador u). El código anterior podría escribirse así:

auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; }); 

Lo cual es mucho más fácil de leer y escribir.Muchas bibliotecas usan la función pipable para los rangos, pero también podría expandirse a otras clases. Boost lo usa en su biblioteca range, pstade oven lo usa, y también esta biblioteca C++ linq lo usa también.

Si desea escribir su propia función pipable, amplíe explica cómo hacerlo here. Sin embargo, otras bibliotecas proporcionan adaptadores de funciones para facilitarlo. El huevo Pstade tiene un pipable adaptor, y linq proporciona el adaptador range_extension para crear una función pipable para rangos como mínimo.

mediante LINQ, primero acaba de crear su función como un objeto función como esta:

struct contains_t 
{ 
    template<class Range, class T> 
    bool operator()(Range && r, T && x) const 
    { return (r | linq::find(x)) != boost::end(r); }; 
}; 

A continuación se inicializa la función usando la inicialización estático como sigue:

range_extension<contains_t> contains = {}; 

A continuación, puede utilizar su función pipable como esta:

if (numbers | contains(5)) printf("We have a 5"); 
Cuestiones relacionadas