在许多情况下,问题甚至没有问自己,因为有时继承提供了模板无法提供的必要功能。例如,当我需要通过一个基类型(多态)处理不同的类型时,我需要使用继承。
但是,在某些情况下,问题可以通过继承和模板来解决。
例如,对代码的某些部分进行类似策略模式的参数化:
文件解析器的一个解决方案可能如下所示:
class FileParser
{
//...
public:
void Parse(ParsingAlgorithm* p);
//...
}
void FileParser::Parse(ParsingAlgorithm* p)
{
m_whatevertypeofvaluesineed = p->Parse(whateverparametersyouneed);
}
其中ParsingAlgorithm是一个抽象基类,它提供了一些基本方法,需要由任何喜欢为FileParser类实现特定解析器的人继承。
但是,使用模板可以轻松实现相同目的:
template <class Parser>
class FileParser
{
//...
public:
void Parse()
{
m_whatevertypeofvaluesineed = m_parser.Parse(whateverparametersyouneed);
}
private:
Parser m_parser;
//...
}
我是否可以使用一些通用规则来决定是使用模板还是继承?或者我应该尽可能简单地使用模板,以避免虚拟函数之类的运行时开销?
答案 0 :(得分:18)
如果您在编译期间知道要操作的对象是什么,那么使用模板的静态多态通常是最快的方法,它会产生更简洁的代码(不需要显式继承)。它也可以更通用,因为您不限于强类层次结构。
如果你想要运行时多态,那么你别无选择,只能使用指针,继承和虚函数的轻微开销。
我自己的意见:
答案 1 :(得分:4)
模板提供编译时多态性,而不是继承提供的运行时多态性。我尽可能使用模板。
Templates and Inheritance上的这篇文章详细解释了它。
答案 2 :(得分:3)
模板在运行时通常更具性能,因为在编译时需要完成更多工作。另一方面,它们可能更复杂,因此难以编写和理解。因此,当它不能使整个解决方案过于复杂时,最好在和时使用它们。
对于您的示例,它们在功能上并不完全等效:第一个变量允许和要求您在运行时创建解析器的实例,而第二个变体需要程序员在他选择解析器时写代码。
答案 3 :(得分:-1)
我的建议是在这种情况下既不使用也不为每种文件类型使用单独的函数;
Stream stream = open(filepath);
Image image = ParseBMP(stream);
// ...or
Image image = ParseJPG(stream);
不需要让事情变得复杂。