在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();
}
如何在不创建派生类的情况下添加方法?我的目标是保留'Abc'名称并为其添加方法。我使用的第三方库中的特定类缺少一些方法,我想添加这些方法,但我保持源文件未编辑。
有办法做到这一点吗?我是编写C ++代码的新手。我熟悉它的一些语法,但不太了解。
答案 0 :(得分:22)
没有。 C++
中无法进行此类扩展。您可以继承您的类并在其中添加新函数。
//Abc.h
class Abc {
void methodOne();
void methodTwo();
}
//Abc+Ext.h
class AbcExt : public Abc {
void methodAdded();
}
然后您可以按如下方式调用方法:
auto *obj = std::make_unique<AbcExt>();
obj->methodOne(); // by the virtue of base class
obj->methodAdded(); // by the virtue of derived class
答案 1 :(得分:5)
有一种方法可以实际执行此操作,但它需要编译器支持#include_next
。海湾合作委员会有这个,不知道其他编译器。它还需要至少支持C ++ 11。
我不会把这个技巧称为漂亮,但它确实起到了作用。
确保您的包含路径具有“扩展”文件所在的目录,该目录位于原始代码所在的目录之前(即原始Abc.hpp
位于src
中,然后将其移至{{ 1}})。因此,在这种情况下,您的包含目标将是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_
的每个翻译单元中创建新的匿名命名空间。如果你的基类有例如,那将导致问题。静态成员。
编辑:没关系,赋值运算符(即Abc.hpp
)也有效,因为Abc bla = evil::Abc(9)
可以隐式转换为evil:Abc
,因为该构造函数存在。< / p>
编辑2:一旦涉及到嵌套的命名空间,您可能会遇到很多麻烦。只要原始Abc
中有#include
,就会发生这种情况,因为它现在将嵌套在Abc.hpp
命名空间中。如果您知道所有包含,则可以在声明evil
命名空间之前包含它们。事情变得非常丑陋,但真的很快。
答案 2 :(得分:3)
您无法延长课程Abc
,期间!
唯一的出路是像
这样的独立功能Abc add(const Abc& a, int b);
答案 3 :(得分:1)
在当前的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&);
};
方法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&);
};
方法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";
}
};
答案 4 :(得分:1)
我发现 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();
}
}
您甚至可以在其他模块命名空间中以不同方式扩展 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.
}
}
如果 class Abc
在全局命名空间中,虽然我还没有尝试过,但我认为您可以将 LibaryA::Abc
替换为 ::Abc
。
很抱歉我的回答太晚了,我已经采用这种方法大约 4 年了,它的结构非常有用。
我在 c++14
中尝试过这个,但我认为这在 c++11
中仍然可行。现在我使用了 c++17
并且它编译得很好。我打算转换为 c++20
当我使用的编译器已经完成 c++20
功能时。