我想用A
和b
两种方法编写课程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
方法中。
答案 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
答案 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