最近我发现了一个更容易进行模板特化而不是真正继承的情况。派生类只需实现一个纯虚函数,并且没有自己的成员。它类似于:
#include <iostream>
class Interface {
public:
virtual void calculate() = 0;
virtual float getResult() = 0;
};
class Base : public Interface {
float result;
public:
Base() : result(1) {};
virtual ~Base() {};
virtual void calculate();
virtual float getValue() = 0; // do some very complex calculation here
float getResult() { return result; }
};
class DerivedA : public Base {
public:
DerivedA() : Base() {};
~DerivedA() {};
float getValue();
};
class DerivedB : public Base {
public:
DerivedB() : Base() {};
~DerivedB() {};
float getValue();
};
void Base::calculate() {
for (int i = 0; i < 10; i++)
result += getValue();
}
float DerivedA::getValue() {
return 1;
}
float DerivedB::getValue() {
return 1.1;
}
int main() {
Interface * a = new DerivedA();
a->calculate();
Interface * b = new DerivedB();
b->calculate();
std::cout << "Result A: " << a->getResult() << std::endl;
std::cout << "Result B: " << b->getResult() << std::endl;
delete a;
delete b;
}
这可以写成专门的模板:
#include <iostream>
class Interface {
public:
virtual void calculate() = 0;
virtual float getResult() = 0;
};
template<typename T>
class Base : public Interface {
float result;
public:
Base() : result(1) {};
void calculate();
float getValue(); // do some very complex calculation here
float getResult() { return result; };
};
typedef Base<int> DerivedA; // actually int and float are only examples
typedef Base<float> DerivedB; // and may be some much more complex types!
template<typename T>
void Base<T>::calculate() {
for (int i = 0; i < 10; i++)
result += getValue();
}
template<typename T>
float Base<T>::getValue() {
return 0;
}
template<>
float Base<int>::getValue() {
return 1;
}
template<>
float Base<float>::getValue() {
return 1.1;
}
int main() {
Interface * a = new DerivedA();
a->calculate();
Interface * b = new DerivedB();
b->calculate();
std::cout << "Result A: " << a->getResult() << std::endl;
std::cout << "Result B: " << b->getResult() << std::endl;
delete a;
delete b;
}
两个示例都给出了相同的结果,我猜第二个示例更快,因为不需要计算虚拟表(在第二种情况下,方法getValue()甚至可以内联)。
所以我的问题是:使用模板专业化而不是继承有什么限制?有没有我见过的副作用?继承优于模板专业化的任何好处?我知道我不能为专门的类创建新的成员和方法,因为我可以为派生创建。但对于这种用例,我只需要实现一些特定于类型的代码,这是一种通用的,更高效的方法吗?
顺便说一下:这个模式有名字吗?
答案 0 :(得分:6)
模板和继承不可互换。
模板表达静态多态(即编译时的多态)
继承允许运行时多态:您可以操作Base
类指针,并期望运行时为您调用正确的虚函数。
使用模板方法,如果您想操纵Base<>
个对象的容器(例如std::vector<Base<??>>
)并在其上调用calculate()
,该怎么办?你不能。
尽管继承和模板都表达了接口和多态,但它们实际上是不同的动物:选择一个而不是依赖于您的上下文,以及如何使用类型。
注意:强>
性能考虑因素不应改变此选择
答案 1 :(得分:0)
使用超类和子类时,DerivedA或DerivedB可以传递给非模板化函数或带有Base类实例的方法。
void method(Base &base)
{
// ...
}
基于模板的方法的主要限制是不再可能。 method()也必须是一个模板:
template<typename T>
void method(Base<T> &base)
{
// ...
}
如果method()很大,那么这里会有相当大的代码膨胀。