具有多次调用的可替换方法的类

时间:2018-04-10 17:46:22

标签: c++ performance function oop polymorphism

我想用Ab两种方法编写课程c。 方法b调用c。 我希望能够用我喜欢的任何内容替换c

如果我将c作为函数指针或虚方法,我认为我可以这样做。

但是,b将被多次调用。这两种解决方案会影响性能吗?

是否可以重新设计它以避免使用函数指针/虚方法?

测试继承:

#include <iostream>
class A { 
public:
    A() {}
    void b() { c(); }
    void c() { std::cout << "1\n"; }
};

class B : public A { 
public:
    void c() { std::cout << "2\n"; }
};

int main() {
    B b;
    b.b();
}

结果:

1

不是2。

编辑:我想指定c在编译时执行的操作。我希望将多种形式的c插入到相同的b方法中。

4 个答案:

答案 0 :(得分:3)

虚函数和函数指针都存在相同的性能问题。

首先,编译器通常无法内联这些调用。通常编译器必须进行实函数调用并假设副作用,因此无法执行许多优化技术。通常影响非常大。

其次,在代码执行期间,CPU通常无法通过指针调用预取代码 - 因为它不知道执行将被转移到何处。但是,硬件调用devirtualizers,类似于分支预测器,尝试根据过去的性能推测性地预取指令,并且在几次调用之后,它们被训练并在那里做得很好。然而,缺乏内联仍然会让你感到厌烦。

答案 1 :(得分:1)

如果您不想要性能下降,可以使用CRTP

template <typename Derived>
class A { 
public:
    A() {}
    void b() { static_cast<Derived*>(this)->c(); }
    void c() { std::cout << "1\n"; }
};

class B : public A<B> { 
public:
    void c() { std::cout << "2\n"; }
};

int main() {
    B b;
    b.b();
}

实例:https://onlinegdb.com/SJzxbtqjz

然而,这可能不是你想要的 - 这一切都取决于你的用例,我无法从你的问题中说出来。请务必阅读我上面链接的维基百科页面上的陷阱部分:

  

静态多态性的一个问题是不使用通用   基类类似于上面例子中的“Shape”,派生类不能   由于每个CRTP基类是唯一类型,因此可以均匀存储。对于   这个原因,从共享基类继承更为常见   使用虚拟析构函数,如上例所示。

编辑:

否则,你需要按照你的建议去做,并付出通过函数指针的代价。但是,如果您调用的函数不是很简单,那么您可能不会看到性能上的差异。

编辑2:

编译器将使用适当的优化完全内联对c()的调用。

带有-O2优化标志的GCC 7.3和更简单的代码(只是为了避免std::cout的复杂性):

template <typename Derived>
class A { 
public:
    A() {}
    int b() { return static_cast<Derived*>(this)->c(); }
    int c() { return 1; }
};

class B : public A<B> { 
public:
    int c() { return 2; }
};


int f() {
    B b;
    return b.b();
}

不会为类B生成任何代码,并为f生成以下程序集:

f():
  mov eax, 2
  ret

实例:https://godbolt.org/g/Mh99bZ

答案 2 :(得分:0)

模板解决方案:

#include <iostream>

struct X1 {
    static void c() { std::cout << "1\n"; }
};

struct X2 {
    static void c() { std::cout << "2\n"; }
};

template <class X>
class A {
public:
    A() {}
    void b() { X::c(); }
};

int main() {
    A<X1> a;
    a.b();
    A<X2> b;
    b.b();
}

结果:

1
2

答案 3 :(得分:0)

参考和模板的解决方案。

这比模板更好,因为方法c可以使用类X1中的属性。

#include <iostream>
class X1 {
public:
    X1(int v) { _v = v; }
    void c() { 
        std::cout << "X1: " << _v << "\n"; 
    }
    int _v;
};

template <typename X>
class A {
public:
    A(X& x) : _x(x) {}
    void b() { _x.c(); }
private:
    X& _x;
};
template <typename X>
A<X> make_A(X& x) { return A<X>(x); }
int main() {
    X1 x1(10);
    // A a1(x1);  ///< need c++17
    auto a = make_A(x1);  ///< works for c++11
    a.b();
}

结果:

X1: 10