C ++类扩展

时间:2009-10-25 17:40:27

标签: c++ class extension-methods

有没有办法向类中添加新方法,而不修改原始类定义(即包含类和相应的.h文件的已编译.lib),如C#的类扩展方法?

9 个答案:

答案 0 :(得分:17)

没有。 C ++没有这样的功能。

正如其他答案所述,常见的解决方法是:

  • 定义派生类,可能使用工厂来隐藏实际的实现类
  • 定义decorator
  • 定义对类
  • 实例进行操作的非成员函数

答案 1 :(得分:11)

不,你不能在C ++中这样做。

如果你想达到这样的目的,你有两个选择,

  • 您可以继承自该类(如果这是一个选项,它可能不合法,因为该类可能没有被编写为允许继承)。
  • 您可以编写自己的包装类,它具有相同的接口+新方法,并委托给您想要扩展的方法。

我更喜欢代表团的做法。

答案 2 :(得分:5)

C#类扩展方法主要是语法糖。您可以使用自由函数获得相同的功能(即,具有对类的引用或常量引用的函数作为其第一个参数)。既然这对STL很有效,为什么不为你的班级?

答案 3 :(得分:3)

在C ++中,您可以使用自由函数,但有时在将多个函数嵌套在一起时,扩展方法的效果会更好。看看这个C#代码:

var r = numbers.Where(x => x > 2).Select(x => x * x);

如果我们使用自由函数在C ++中编写它,它将如下所示:

auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; });

这不仅难以阅读,而且难以编写。解决这个问题的常用方法是创建所谓的pipable函数。这些函数是通过重载|管道运算符(它实际上是或运算符)来创建的。所以上面的代码可以这样写:

auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; });

这更易于阅读和书写。许多库对范围使用pipable函数,但也可以扩展到其他类。 Boost在range库中使用它,pstade oven使用它,此C ++ linq库也使用它。

如果您想编写自己的pipable函数,请解释如何执行此操作here。但是,其他库提供了功能适配器以使其更容易。 Pstade egg有一个pipable adaptor,linq提供range_extension适配器来为范围创建一个pipable函数。

使用linq,首先只需将函数创建为函数对象,如下所示:

struct contains_t
{
    template<class Range, class T>
    bool operator()(Range && r, T && x) const
    { return (r | linq::find(x)) != boost::end(r); };
};

然后使用静态初始化初始化函数,如下所示:

range_extension<contains_t> contains = {};

然后你可以像这样使用你的pipable函数:

if (numbers | contains(5)) printf("We have a 5");

答案 4 :(得分:1)

一般不是。但是,如果库不创建需要您的扩展的类的实例,并且您能够修改应用程序中创建类的实例并需要扩展的所有位置,那么您可以采用以下方式:

  • 创建一个工厂函数,该函数在需要该类实例的所有位置调用,并返回指向该实例的指针(google for Design Patterns Factory ,...)。
  • 使用所需的扩展名创建派生类。
  • 使工厂函数返回派生类而不是原始类。

示例:


    class derivedClass: public originalClass { /* ... */};

    originalClass* createOriginalClassInstance()
    {
         return new derivedClass();
    }
  • 当您需要访问扩展时,您需要将原始强制转换投射到派生类。当然。

这大致是如何实现Glen建议的“继承”方法。从理论的角度来看,Glen的“具有相同界面的包装类”方法也非常好,但具有稍微不同的属性,使得在您的情况下工作的可能性降低。

答案 5 :(得分:1)

有一种方法可以完成。这是通过放松你的要求一点。在C ++中,人们经常说类的接口不仅包括其成员函数,还包括所有在类上工作的函数。

也就是说,可以将类作为参数赋予的非成员函数应该被视为其接口的一部分。

例如,std::find()std::sort()std::vector接口的一部分,即使它们不是该类的成员。

如果你接受这个定义,那么你总是可以通过添加非成员函数来扩展一个类。

答案 6 :(得分:0)

您无法将方法或数据物理地添加到二进制形式的类文件中。但是,您可以通过编写扩展类来向该类的对象添加方法和数据(功能和状态)。这不是直截了当的,需要基于元对象协议和接口的编程。你需要做很多事情才能在C ++中实现这一点,因为它不支持反射开箱即用。在这种实现中,当您通过原始类对象指针查询由新扩展类实现的接口时,元对象实现通过元类对象返回该接口指针,该元类对象用于在运行时创建的扩展类。 这是多少可定制(基于插件)的软件应用程序框架的工作原理。但是,您必须记住,它需要将许多其他MOP机制写入使用字典的所有类的实例化元对象,其中描述了对象关系,并为原始和扩展类对象提供正确的接口指针。 Dassault Systemes的CATIA V5是用这种称为CAA V5的架构编写的,您可以通过编写具有所需功能的新扩展类来扩展现有组件。

答案 7 :(得分:-2)

抱歉,没有。一旦你的代码在obj中,你就无法改变它。如果可以在VC中完成此操作,则已经支持部分类。但有一个例外,运算符方法可以使用全局函数进行扩展,非常类似于cout&lt;&lt;在STL中实施。

答案 8 :(得分:-3)

当然可以:


template <typename Ext>
class Class: public Ext { /* ... */ };

但这并不意味着它是最好的方法。