Es posible en C++ para extender una clase(agregar un método) en un archivo de origen diferente sin necesidad de editar el archivo fuente original, donde la clase que está escrito?

En obj-c es posible por escribir otro @interface AbcClass (ExtCategory) ... @end

Tengo error en tiempo de compilación(s) cuando he intentado algo como esto:

//Abc.h
class Abc {            //This class is from a 3rd party library....
                       // ...I don't want to edit its source file.
    void methodOne();
    void methodTwo();

}


//Abc+Ext.h
class Abc {       //ERROR: Redefinition of 'Abc'
    void methodAdded();
}

¿Cómo puedo agregar un método sin necesidad de crear una clase derivada? Mi objetivo es retener el ‘Abc’ nombre y añadir métodos a ella. Una clase específica en una 3ª parte de la biblioteca que he utilizado carece de algunos de los métodos y quiero agregar esos métodos, pero estoy teniendo el archivo de origen inéditas.

Hay una manera de hacer esto? Soy nuevo en la escritura de C++ códigos. Estoy familiarizado con algunos de sus sintaxis, pero no sé mucho.

  • A nombre de otro idioma, C# hace posible dividir la definición de una clase en más de un archivo a través de un mecanismo conocido como parcial de la clase.
InformationsquelleAutor acegs | 2013-09-14

4 Comentarios

  1. 23

    No. Este tipo de extensión de la clase no es posible en C++. Usted puede heredar de la clase y añadir nuevas funciones en allí, sin embargo.

    //Abc.h
    class Abc {
        void methodOne();
        void methodTwo();
    }
    
    
    //Abc+Ext.h
    class AbcExt : public Abc {       
        void methodAdded();
    }

    Puede llamar a métodos como los siguientes:

    auto *obj = std::make_unique<AbcExt>();
    obj->methodOne(); //by the virtue of base class
    obj->methodAdded(); //by the virtue of derived class
    • Por favor, evite los nuevos
    • Me tomé la libertad de editar…
    • El puntero es totalmente innecesaria para demostrar el punto.
    • AbcExt obj; obj.methodOne(); obj.methodAdded();
  2. 5

    Hay una manera de hacer esto, pero requiere que el compilador de apoyo #include_next. GCC tiene, ni idea acerca de otros compiladores. También se necesita el apoyo de al menos C++11.

    No me llama exactamente este truco bonito, pero hace el trabajo.

    Asegúrese de incluir su dirección tiene el directorio donde la «extensión» reside el archivo antes de que el directorio donde está el código original reside (es decir, si el original Abc.hpp es en src, a continuación, desplazarse a src/some_dir). En este caso, la incluyen dirs sería -Isrc -Isrc/some_dir.

    Su «extensión» código debe estar en un archivo con el mismo nombre que el código original. Así que para este ejemplo que Abc.hpp.

    Aquí está el archivo de extensión del contenido:

    #ifndef ABC_EXT_HPP_
    #define ABC_EXT_HPP_
    
    #include <utility>
    
    namespace evil {
      //Search the include path for the original file.
      #include_next "Abc.hpp"
    }
    
    class Abc : public evil::Abc {
      public:
        /*
        //Inherit all constructors from base class. Requires GCC >=4.8.
        using evil::Abc::Abc;
        */
    
        /* Use the solution below if your compiler supports C++11, but not 
         * inheriting constructors.
         */
        template <class... Args>
        Abc (Args... args) : evil::ABC(std::forward<Args...>(args...)) { }
    
        ~Abc () { }
    
        void methodAdded () { /* Do some magic. */ }
    };
    
    #endif //ABC_EXT_HPP_

    Hay cosas que faltan en el ejemplo como el operador de asignación no ser «enviado» a la clase base. Usted puede utilizar el mismo truco que se utiliza para el constructor para hacer eso. Hay otras cosas que faltan, pero esto te dará un punto de partida que funciona lo suficientemente bien como para «simple» de clases.

    Una cosa que no me gusta es la creación de la «maldad» de espacio de nombres. Sin embargo, anónimo espacios de nombres no puede ayudar aquí, porque un nuevo espacio de nombres anónimos se creará en cada unidad de traducción que incluye Abc.hpp. Que va a llevar a problemas si la clase base tiene por ejemplo, que los miembros estáticos.

    Edición: Nevermind, el operador de asignación (es decir,Abc bla = evil::Abc(9)) también funciona, porque evil:Abc se puede convertir implícitamente Abc debido a que el constructor existe.

    Edit 2: puede ejecutar en un montón de problemas para una vez que no hay los espacios de nombres anidados involucrados. Esto sucede tan pronto como hay una #include en el original Abc.hpp, porque ahora va a estar anidada dentro de la evil espacio de nombres. Si usted sabe todos los incluye, usted podría incluir en ellos antes de declarar la evil espacio de nombres. Las cosas se ponen muy feo, muy rápido aunque.

  3. 3

    No se puede extender la clase Abc, y punto!

    La única manera de salir son independientes de las funciones como

    Abc add(const Abc& a, int b);
    • Se puede dar el ejemplo de código?
    • En realidad, no: uno común es el Abc operator + (const Abc&, const T&);
    • Es difícil dar formato a una multi-línea de código en los comentarios, así que sólo he de hacer más simple y en línea. Basado en el código de ejemplo, puedo llamar a la función add() como este: abcOne.add(5)? o esto: add(abcOne, 5)?
    • sería el último, por desgracia
  4. 1

    No hay ningún mecanismo específico para hacerlo directamente en la corriente de C++, pero hay varias maneras que usted puede lograr algo parecido en el costo de algunos de calderas de la placa de trabajo:

    Método 1:

    //foo.h
    class Foo {
    private:      //stuff
    public:       //stuff
    
    private:
        //All this crap is private. Pretend like I didn't expose it.
        //yeah, I know, you have to compile it, and it probably adds
        //dependencies you don't want to #include, like <string>
        //or boost, but suck it up, cupcake. Stroustrup hates life.
        void internalHelper(std::string&, std::vector&, boost::everything&);
    };

    Método 2:

    //foo.h
    class Foo {
    private:      //stuff
    public:       //stuff
    };
    
    //fooimpl.h
    //Internal file, do not export with the API.
    class FooImpl : public Foo {
    private:      //stuff
    public:       //stuff
        //So yeah, you have to go thru a cast and an extra include
        //if you want to access this. Suck it up, cupcake.
        void internalHelper(std::string&, std::vector&, boost::everything&);
    };

    Método 3:

    //foo.h
    class Foo {
    private:      //stuff
    public:       //stuff
    
        //For the private api: this is the worst approach, since it
        //exposes stuff and forces include/cruft on consumers.
        friend void foo_internalHelper(std::string&, std::vector&, boost::everything&);
    };
    
    //foo.cpp
    
    //don't make it static or anyone can make their own as a way to
    //back door into our class.
    void foo_internalHelper(...);

    Método 4:

    //foo.h
    class Foo {
    private:      //stuff
    public:       //stuff
    
        //No dependencies, but nothing stops an end-user from creating
        //a FooPrivate themselves...
        friend class FooPrivate;
    };
    
    //foo1.cpp
    class FooPrivate {
    public:
        void fooInternalHelper(Foo* f) {
           f->m_privateInternalYouCantSeeMe = "Oh, but I can";
        }
    };
    • Esto es demasiado malo – no me puedo resistir -1
    • Qué parte?
    • El general hipo !?
    • (He quitado las cosas acerca de mi propuesta, creo que es probablemente inapropiado para una respuesta)
    • Cambió a «internalHelper».
    • que merece un -2
    • Gracias por tu respuesta. Mi objetivo es no editar el archivo fuente original donde la clase está escrito y yo todavía uso el original de la clase cuando se llama el añadido de método. Quiero compartir mi código a otras personas sin la molestia de actualizar el archivo de origen original y la creación de una instancia de una clase derivada con nombre diferente acaba de llamar el añadido de método. En Obj-C, esto es posible. Espero que en C++ demasiado.
    • No veo por qué no – «internalHelper» es una descripción acertada de lo que las personas suelen hacerlo. E. g. si usted pone su aplicación en uno .archivo cpp y desea un registro estándar de formato de línea (void Foo::logHelper(const char* msg) { printf("[%s][%s:%u] c:%u a:%u %s\n", getTime(), user(), port(), connection(), accountID(), msg);)

Dejar respuesta

Please enter your comment!
Please enter your name here