将方法添加到现有的C++;在其他文件中初始化 < C++ >是否可以在不编辑原始类文件的情况下扩展一个不同的源文件中的一个类(添加方法)?< /p>

将方法添加到现有的C++;在其他文件中初始化 < C++ >是否可以在不编辑原始类文件的情况下扩展一个不同的源文件中的一个类(添加方法)?< /p>,c++,C++,在obj-c中,可以通过编写另一个接口AbcClass(ExtCategory)@结束 我在尝试以下操作时出现编译时错误: //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();

在obj-c中,可以通过编写另一个接口AbcClass(ExtCategory)@结束

我在尝试以下操作时出现编译时错误:

//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();
}
我的目标是保留“Abc”名称并向其添加方法。我使用的第三方库中的一个特定类缺少一些方法,我想添加这些方法,但我不编辑源文件


有办法做到这一点吗?在编写C++代码时我是新手。我熟悉它的一些语法,但知道的不多。

不。这种类扩展在
C++
中是不可能的。但您可以从原始源文件继承类,并在源文件中添加新函数

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


//Abc+Ext.h
class AbcExt : public Abc {       
    void methodAdded();
};
然后可以按如下方式调用方法:

std::unique_ptr<AbcExt> obj = std::make_unique<AbcExt>();
obj->methodOne(); // by the virtue of base class
obj->methodAdded(); // by the virtue of derived class
std::unique_ptr obj=std::make_unique();
obj->methodOne();//凭借基础阶级的力量
obj->methodAdded();//借助于派生类

在当前C++中,没有直接的机制来做这件事,但是有几种方法可以以某些锅炉板工作为代价来实现它:

方法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&);
};
方法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(...);
方法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";
    }
};

您不能扩展类
Abc
,句点

唯一的出路是独立的功能,如

Abc add(const Abc& a, int b);

有一种方法可以做到这一点,但它需要编译器支持
#include_next
。GCC有这个功能,不知道其他编译器。它还需要至少支持C++11

我不认为这个把戏很漂亮,但它确实起到了作用

确保您的包含路径在原始代码所在的目录之前包含“扩展名”文件所在的目录(即,如果原始
Abc.hpp
位于
src
,则将其移动到
src/some\u dir
)。因此,在本例中,您的include 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_
\ifndef ABC\u EXT\u水电站_
#定义ABC_EXT_水电站_
#包括
名称空间邪恶{
//搜索原始文件的包含路径。
#包括下一个“Abc.hpp”
}
Abc类:公害::Abc{
公众:
/*
//从基类继承所有构造函数。要求GCC>=4.8。
使用邪恶::Abc::Abc;
*/
/*如果您的编译器支持C++11,但不支持
*继承构造函数。
*/
模板
Abc(Args…Args):邪恶::Abc(std::转发(Args…){}
~Abc(){}
void methodAdded(){/*做一些魔术。*/}
};
#endif//ABC\u EXT\u水电站_
示例中缺少一些内容,例如赋值运算符没有“转发”到基类。您可以使用与构造函数相同的技巧来实现这一点。可能还缺少其他东西,但这应该为您提供一个起点,该起点对于“简单”类来说已经足够好了

我不喜欢创建“邪恶”名称空间。但是,匿名名称空间在这里无能为力,因为每个翻译单元都将创建一个新的匿名名称空间,其中包括
Abc.hpp
。如果基类中有静态成员,则会导致问题

Edit:Nevermind,赋值运算符(即
Abc bla=evil::Abc(9)
)也可以工作,因为
evil:Abc
可以隐式转换为
Abc
,因为该构造函数存在


编辑2:一旦涉及嵌套名称空间,您可能会遇到很多麻烦。当原始
Abc.hpp
中有
#include
时,就会发生这种情况,因为它现在将嵌套在
evil
命名空间中。如果您知道所有包含项,那么可以在声明
evil
名称空间之前包含它们。事情变得非常糟糕,但是非常快。

我发现
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();
    }
    
}
您甚至可以在其他模块名称空间中以不同方式扩展
类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.
    }
    
}
如果
类Abc
在全局命名空间中,尽管我还没有尝试过,但我认为您可以将
LibaryA::Abc
替换为
::Abc

很抱歉,我这么晚才回答这个问题,我已经做了大约4年了,它的结构非常有用。 我在
c++14
中尝试了这一点,但我认为这在
c++11
中仍然是可行的。现在我使用了
c++17
,它编译得很好。我计划转换成
c++20

当我使用的编译器已经完成了
c++20
功能时。

用另一种语言命名,c#可以通过一种称为a的机制将一个类的定义拆分到多个文件上。这太糟糕了-我无法抗拒-1@DieterL你看哪一部分?总的打嗝!?(我删除了关于我的提案的内容,我认为这样的回答可能不合适)@kfsone:谢谢你的回答。我的目标是不编辑编写类的原始源文件,并且在调用添加的方法时仍然可以使用原始类。我希望将我的代码共享给其他人,而不必为了调用添加的方法而更新原始源文件和创建具有不同名称的派生类的实例。在Obj-C中,这是可能的。我也希望C++。@ XXWICHA不是真的:一个常见的是ABC操作符+(const abc,const t &);很难在注释中格式化多行代码,所以我只是让它更简单和内联。根据您的示例代码,我可以这样调用add()吗:
abcOne.add
//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.
    }
    
}