将方法添加到其他文件中的现有C++类

ace*_*egs 32 c++

在C++中是否可以在不编辑创建类的原始源文件的情况下在不同的源文件中扩展类(添加方法)?

在obj-c中,可以通过写另一个来实现 @interface AbcClass (ExtCategory) ... @end

我尝试这些时出现编译时错误:

//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();
}
Run Code Online (Sandbox Code Playgroud)

如何在不创建派生类的情况下添加方法?我的目标是保留'Abc'名称并为其添加方法.我使用的第三方库中的特定类缺少一些方法,我想添加这些方法,但我保持源文件未编辑.

有没有办法做到这一点?我是编写C++代码的新手.我熟悉它的一些语法,但不太了解.

Man*_*thi 27

不可以C++.这种类扩展是不可能的.您可以继承您的类并在其中添加新函数.

//Abc.h
class Abc {
    void methodOne();
    void methodTwo();
};


//Abc+Ext.h
class AbcExt : public Abc {       
    void methodAdded();
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以调用以下方法:

auto *obj = std::make_unique<AbcExt>();
obj->methodOne(); // by the virtue of base class
obj->methodAdded(); // by the virtue of derived class
Run Code Online (Sandbox Code Playgroud)

  • 完全不需要指针来证明这一点. (20认同)
  • 请避免新的 (9认同)
  • `AbcExt obj; obj.methodOne(); obj.methodAdded();` (4认同)

Dar*_*uuk 6

有一种方法可以执行此操作,但是需要编译器支持#include_next。GCC有这个,对其他编译器一无所知。它还至少需要支持C ++ 11。

我不会把这个技巧说得很漂亮,但是确实可以。

确保include路径有所在的目录的“扩展”文件所在的目录之前,其中的原代码所在(即如果原来Abc.hppsrc,然后移动它src/some_dir)。因此,在这种情况下,您的包含目录将为-Isrc -Isrc/some_dir

您的“扩展名”代码应位于与原始代码名称完全相同的文件中。因此,此示例为Abc.hpp

这是扩展文件的内容:

#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_
Run Code Online (Sandbox Code Playgroud)

该示例中缺少一些东西,例如赋值运算符未“转发”到基类。您可以使用与构造函数相同的技巧。可能还缺少其他东西,但这应该为您提供一个起点,该起点对于“简单”类足够好。

我不喜欢的一件事是创建“邪恶的”命名空间。但是,匿名名称空间在这里无济于事,因为将在每个包含的翻译单元中创建一个新的匿名名称空间Abc.hpp。如果您的基类具有例如静态成员,则将导致问题。

编辑:没关系,赋值运算符(即Abc bla = evil::Abc(9))也可以使用,因为evil:Abc可以隐式转换Abc为该构造函数的存在。

编辑2:一旦涉及嵌套的名称空间,您可能会遇到很多麻烦。这种情况会#include在原件中立即出现Abc.hpp,因为它现在将嵌套在evil名称空间中。如果您知道所有包含,则可以在声明evil名称空间之前将它们包含在内。事情变得非常丑陋,但是很快。


ace*_*egs 3

我发现 这样c++做比 更好obj-c

我尝试了以下方法,效果很好!

关键是,将所有类包含在命名空间中,然后使用相同的类名扩展目标类。

//Abc.h
namespace LibraryA {
    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.hpp
namespace MyProj {
    class Abc : public LibraryA::Abc {
        using Base = LibraryA::Abc;   //desc: this is to easily access the original class...
                                      //   ...by using code: Abc::Base::someOrigMethod();
        using Base::Base;             //desc: inherit all constructors.
        
    protected:
        //---added members:
        int memberAdded;
        
    public:
        //---added methods:
        void methodAdded();
        
        //---modified virtual member funcs from original class.
        void origMethod_A() override;
        
    }
}

//Abc+Ext.cpp
namespace MyProj {
    void Abc::origMethod_A() {
        //...some code here...
        Base::origMethod_A();    //desc: you can still call the orignal method
        //...some code here...
    }
}

//SomeSourceFile_ThatUses_Abc.cpp
namespace MyProj {      //IMPT NOTE: you really need to enclose your...
                        //   ...project specific code to a namespace so you can...
                        //   ...use the version of class Abc you extended.
                        
                        
    void SomeClass::SampleFunc(){
        Abc objX;                   //create MyProj::Abc object.
        objX.methodAdded();         //calls MyProj::Abc::methodAdded();
        objX.origMethod_A();        //calls MyProj::Abc::origMethod_A();
        
        Abc::Base objY;             //create Library::Abc object.
        //objY.methodAdded();       //method not existing.
        objY.origMethod_A();        //calls Library::Abc::origMethod_A();
        
        //...some code here...
    }
    
}

//SomeModule.cpp
namespace OtherNamespace {
    void SomeOtherClass::SampleOtherFunc(){
        Abc objZ;                   //create Library::Abc object.
        //objZ.methodAdded();       //method not existing.
        objZ.origMethod_A();        //calls LibraryA::Abc::origMethod_A();
    }
    
}
Run Code Online (Sandbox Code Playgroud)

您甚至可以class Abc在其他模块命名空间内进行不同的扩展。

//MyLib_ModuleA_Classes.hpp
namespace MyLib_ModuleA {
    class Abc : public LibraryA::Abc {
        //...add extensions here...
        void addedMethod_X();
        void origMethod_A() override;    //own overriden behavior specific to this ModuleA only.
    }
    
}

//MyLib_ModuleB_Classes.hpp
namespace MyLib_ModuleB {
    class Abc : public LibraryA::Abc {
        //...add extensions here...
        void addedMethod_Y();
        void origMethod_A() override;    //own overriden behavior specific to this ModuleB only.
    }
    
}
Run Code Online (Sandbox Code Playgroud)

如果万一class Abc在全局命名空间中,虽然我还没有尝试过,但我认为你可以替换LibaryA::Abc::Abc.

很抱歉这么晚才回答,我已经使用这种方法大约四年了,它的结构非常有用。我尝试过这个c++14,但我认为这在c++11. 现在我使用了c++17并且编译得很好。我打算转换到c++20 我使用的编译器已经完成的c++20功能。