虚拟重载vs`std :: function`成员?

时间:2014-06-11 09:14:36

标签: c++ c++11

在我上课的情况下,我们称之为Generic。这个类有成员和属性,我打算在std::vector<Generic>或类似的地方使用它,处理这个类的几个实例。

另外,我想专门化这个类,泛型和专用对象之间的唯一区别是私有方法,它不访问类的任何成员(但是被其他方法调用)。我的第一个想法是简单地声明它virtual并在专门的类中重载它:

class Generic
{
    // all other members and attributes
    private:
        virtual float specialFunc(float x) const =0;
};

class Specialized_one : public Generic
{
    private:
        virtual float specialFunc(float x) const{ return x;}
};

class Specialized_two : public Generic
{
    private:
        virtual float specialFunc(float x) const{ return 2*x; }
}

因此我想我必须使用std::vector<Generic*>,并动态创建和销毁对象。

一位朋友建议我为std::function<>类使用Generic属性,并将specialFunc作为参数提供给构造函数,但我不确定如何正确执行。< / p>

这两种方法的优点和缺点是什么,还有其他(更好的?)方法可以做同样的事情吗?我对此非常好奇。

有关详细信息,我实例化的每个对象的特化将在运行时确定,具体取决于用户输入。我可能最终会得到很多这些对象(还不确定有多少),所以我想避免任何不必要的开销。

3 个答案:

答案 0 :(得分:5)

virtual函数和重载建立is-a关系,而std::function模拟has-a关系。

使用哪一个取决于您的具体用例。

使用std::function可能更灵活,因为您可以在不引入新类型的情况下轻松修改功能。


性能不应该是这里的主要决策点,除非这个代码可以证明(即你测量过)你程序中的紧密循环瓶颈。

答案 1 :(得分:2)

  

这两种方法的优点和缺点是什么,还有其他(更好的)方法可以做同样的事情吗?

我能看到的问题是&#34;你希望如何定义你的课程?&#34; (例如,什么是公共界面?)

考虑创建这样的API:

class Generic
{
    // all other members and attributes
    explicit Generic(std::function<float(float)> specialFunc);
};

现在,您可以无需关心地创建Generic的任何实例。如果你不知道你将在specialFunc中放置什么,这是最好的选择(&#34;你不知道&#34;意味着你的代码的客户可能在一个月内决定从另一个人那里放置一个功能库那里有一个相同的函数(&#34;接收x,返回x&#34;),访问某个数据库获取值,将有状态函子传递给你的函数,或任何 else)。

此外,如果specialFunc可以更改现有实例(即使用specialFunc创建实例,使用它,更改specialFunc,再次使用它等),您应该使用此变体。

此变体可能会被其他约束强加给您的代码库。 (例如,如果想避免虚拟Generic,或者出于其他原因需要final。)

如果(另一方面)你的specialFunc只能从有限数量的实现中做出选择,而客户端代码以后无法决定他们想要别的东西 - 即你只有相同的功能并且价值加倍 - 就像在你的例子中一样 - 那么你应该依赖于专业化,比如你问题中的代码。

TLDR :根据班级的使用情况决定。

编辑:关于beter(或至少是另类)方法...你可以在你的班级注入一个&#34; per needed&#34;基础:

即代替:

class Generic
{
public:
    Generic(std::function<float(float> f) : specialFunc{f} {}

    void fancy_computation2() { 2 * specialFunc(2.); }
    void fancy_computation4() { 4 * specialFunc(4.); }
private:
    std::function<float(float> specialFunc;
};

你可以这样写:

class Generic
{
public:
    Generic() {}

    void fancy_computation2(std::function<float(float> f) { 2 * f(2.); }
    void fancy_computation4(std::function<float(float> f) { 4 * f(4.); }
private:
};

这为您提供了更大的灵活性(您可以使用单个实例使用不同的特殊功能),代价是更复杂的客户端代码。这也可能是您不想要的灵活性(太多)。

答案 2 :(得分:2)

首先,让我们抛出表现。

如果你使用virtual函数,正如你所说,你可能会得到很多具有相同界面的类:

class generic {
    virtual f(float x);
};

class spec1 : public generic {
    virtual f(float x);
};

class spec2 : public generic {
    virtual f(float x);
};

使用std::function<void(float)>作为成员可以避免所有专业化:

class meaningful_class_name {
    std::function<void(float)> f;

public:
    meaningful_class_name(std::function<void(float)> const& p_f) : f(p_f) {}
};

事实上,如果这只是您使用该类的唯一内容,您可以将其删除,并在调用者级别使用std::function<void(float)>

std::function的优势:

1)更少的代码(N个函数需要1个类,而虚函数需要N个函数的N个类。我假设这个函数是类之间唯一不同的函数)。
2)更多的灵活性(如果你愿意,你可以传递捕捉持有状态的lambda) 3)如果您将类作为模板编写,则可以根据需要将其用于各种函数签名。

使用std::function可以解决您尝试使用virtual函数解决的任何问题,而且似乎做得更好。但是,我并不断言std::function 总是比几个类中的一堆虚函数更好。有时,这些功能必须是私有的和虚拟的,因为它们的实现与任何外部调用者无关,因此灵活性不是优势。

std::function的缺点:

1)我打算写一下你无法访问generic类的私有成员,但后来我意识到你可以修改类本身的std::function捕获持有this的lambda。考虑到你概述课程的方式,这不应该是一个问题,因为它似乎无视任何形式的内部状态。