C++类扩展
有没有办法像C#的类扩展方法一样向类添加新方法,而不需要修改原始类定义(即编译后的包含类和相应.h文件的.lib)?
Is there a way to add new methods to a class, without modifying original class definition (i.e. compiled .lib containing class and corresponding .h file) like C#'s class extension methods?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
不可以。C++ 没有这样的能力。
正如其他答案中提到的,常见的解决方法是:
No. C++ has no such capability.
As mentioned in other answers, the common workarounds are:
不,您不能在 C++ 中执行此操作。
如果你想实现这样的目标,你有两个选择,
我更喜欢委托方式。
No, you can't do this in C++.
If you want to achieve something like this you have 2 options,
I prefer the delegation approach.
C# 类扩展方法主要是语法糖。您可以使用自由函数(即,将类的引用或常量引用作为其第一个参数的函数)获得相同的功能。既然这对于 STL 来说效果很好,为什么不适合你的班级呢?
C# class extension methods are mostly syntactic sugar. You get the same functionality with free functions (i.e., functions with a reference or constant reference to your class as their first parameter). Since this works well for the STL, why not for your class?
在 C++ 中,您可以使用自由函数,但有时,当您将许多函数嵌套在一起时,扩展方法会更有效。看一下这段 C# 代码:
如果我们使用 free 函数在 C++ 中编写它,它将如下所示:
这不仅难以阅读,而且难以编写。解决这个问题的常见方法是创建所谓的 pipable 函数。这些函数是通过重载
|
管道运算符(实际上是 or 运算符)创建的。所以上面的代码可以这样写:这样更容易阅读和编写。许多库使用 pipable 函数来表示范围,但它也可以扩展到其他类。 Boost 在其 中使用它range 库,pstade oven 使用它,而且这个 C++ linq 库也使用它。
如果您想编写自己的 pipable 函数,请 boost 解释如何执行此操作 此处。然而,其他库提供了函数适配器以使其更容易。 Pstade Egg 有一个 pipable适配器,linq提供了
range_extension
适配器来为范围创建至少一个可管道函数。使用 linq,首先将函数创建为函数对象,如下所示:
然后使用静态初始化来初始化该函数,如下所示:
然后您可以像这样使用 pipable 函数:
In C++ you can use free functions, but sometimes extension methods work better when you nest many functions together. Take a look at this C# code:
If we to write this in C++ using free function it would look like this:
Not only is this difficult to read, but it is difficult to write. The common way to solve this is to create what is called a pipable function. These functions are created by overloading the
|
pipe operator(which is just really the or operator). So the code above could be written like this:Which is much easier to read and write. Many libraries use pipable function for ranges, but it could be expanded to other classes as well. Boost uses it in their range library, pstade oven uses it, and also this C++ linq library uses it as well.
If you would like to write your own pipable function, boost explain how to do that here. Other libraries, however, provide function adaptors to make it easier. Pstade egg has a pipable adaptor, and linq provides the
range_extension
adaptor to create a pipable function for ranges as least.Using linq, you first just create your function as a function object like this:
Then you initialize the function using static initialization like this:
Then you can use your pipable function like this:
一般不会。但是,如果库没有创建需要您的扩展的类的实例,并且您能够修改应用程序中创建该类的实例并需要您的扩展的所有位置,那么您可以采用一种方法:
示例:
这大致就是如何实现Glen建议的“继承”方法。从理论角度来看,格伦的“具有相同接口的包装类”方法也非常好,但其属性略有不同,这使得它不太可能在您的情况下工作。
Generally not. However, if the library does not create instances of the class that require your extension and you are able to modify all places in the app that create an instance of the class and require your extensions, there is a way you can go:
Example:
This is roughly how to implement the "inherit" method suggested by Glen. Glen's "wrapper class with same interface" method is also very nice from a theoretical point of view, but has slightly different properties that makes it less probable to work in your case.
有一种方法可以做到这一点。那就是稍微放松你的要求。在 C++ 中,人们经常说类的接口不仅由其成员函数组成,还由在该类上工作的所有函数组成。
也就是说,可以将类作为参数提供的非成员函数应被视为其接口的一部分。
例如,
std::find()
或std::sort()
是std::vector
接口的一部分,即使他们不是班级成员。如果您接受这个定义,那么您总是可以通过添加非成员函数来扩展类。
There is one way in which it can be done. And that's by relaxing your requirements a bit. In C++, people often say that the interface of a class consists not just of its member functions, but of all functions that work on the class.
That is, non-member functions which can be given the class as a parameter should be considered part of its interface.
For example,
std::find()
orstd::sort()
are part of the interface ofstd::vector
, even though they aren't members of the class.And if you accept this definition, then you can always extend a class simply by adding nonmember functions.
您无法将方法或数据物理添加到二进制形式的类文件中。但是,您可以通过编写扩展类向该类的对象添加方法和数据(功能和状态)。这并不简单,需要基于元对象协议和接口的编程。您需要做很多工作才能在 C++ 中实现此目的,因为它不支持开箱即用的反射。在这样的实现中,当您通过原始类对象指针查询新扩展类实现的接口时,元对象实现将通过它在运行时创建的扩展类的元类对象返回该接口指针。
这就是许多可定制(基于插件)软件应用程序框架的工作原理。但是,您必须记住,它需要编写许多其他 MOP 机制来使用描述对象关系的字典实例化所有类的元对象,并为原始类对象和扩展类对象提供正确的接口指针。 Dassault Systemes 的 CATIA V5 采用名为 CAA V5 的架构编写,您可以通过编写具有所需功能的新扩展类来扩展现有组件。
You cannot add methods or data physically to the class file which is in binary form. However, you can add methods and data (functionality and state) to the objects of that class by writing extension classes. This is not straight forward and requires Meta-Object-Protocol and Interface based programming. You need to do a lot to achieve this in C++ since it does not support Reflection out of the box. In such an implementation when you query for the interface implemented by your new extension class via the original class object pointer, the meta object implementation returns that interface pointer via the meta class object for the extension class that it creates at runtime.
This is how many customizable (plugin based) software application frameworks work. However, you must remember that it requires many other MOP mechanisms to be written to instanciate meta objects for all the classes using dictionaries in which the object relations are described and give the correct interface pointers for the original and extended class objects. Dassault Systemes' CATIA V5 is written in such an architecture called CAA V5 where you can extend existing components by writing new extension classes with the desired functionality.
在 C++ 中,成员函数通常被高估。除了我们大多数人很少需要的五六个(如五六个规则)和一些运算符之外,其他所有功能都应该是免费功能。不是“朋友”,只是命名空间范围内的自由函数。
如果您因为自动完成而更喜欢成员,则可以使用适当的命名(C 风格)。例如,对于客户类,自由函数可以是
void customerupdate(customer &, const data&newcustomerdata)
。Member functions are usually overrated in C++. With exception of the five or six (as in the rule of five or six) that most of us rarely need, and some operators, everything else should be a free function. Not "friend", just namespace-scoped free function.
If you prefer members because of autocompletion, you can just use appropriate naming (C-style). e.g. for a customer class, a free function can be
void customerupdate(customer &, const data&newcustomerdata)
.当然可以:
但这并不意味着这是最好的方法。
Sure you can:
That doesn't mean it's the best approach though.
抱歉,没有。一旦您的代码位于 obj 中,您就无法更改它。如果这可以在 VC 中完成,则已经支持部分类了。但有一个例外,运算符方法可以使用全局函数进行扩展,就像 cout<< 一样。是用STL实现的。
Sorry, no. Once your code is in obj, you can not change it. If this can be done in VC partial classes would be supported already. There is one exception though, operator methods can be extended using global functions, pretty like how cout<< is implemented in STL.