现在我正在学习C ++,现在我知道了模板的基本概念, 其行为就像一般类型, 我发现几乎每个c ++程序都使用模板, 所以我真的想知道我们什么时候应该使用模板? 有人可以总结一下我对c ++模板的体验吗? 你什么时候考虑使用模板?
补充: 如果我们定义了这样的功能
template <class myType>
myType GetMax (myType a, myType b) {
return (a>b?a:b);
}
但我们想传递一个对象(自定义类)进行比较,我们该如何实现?
Supplement2: 在下面的答案中,有人写了这个示例代码
template <class myType>
const myType& GetMax (const myType& a, const myType& b) {
return (a<b?b:a);
}
template <class myType, class Compare>
const myType& GetMax (const myType& a, const myType& b, Compare compare) {
return (compare(a,b)?b:a);
}
这是对的吗?我们可以只传递一个函数名作为类myType的参数吗?
答案 0 :(得分:8)
天儿真好,
简单的答案是当你希望行为保持不变时,独立于用于实例化类的类型。
因此,一堆int将以与一堆浮点数将作为MyClass对象堆栈相同的方式运行。
当您希望允许行为专门化时,使用继承和基类。
所以说你有一个名为Animal的基类,它有一个名为makeSound()的成员函数。你不知道每只动物会发出什么样的声音,所以你让makeSound成员函数成为一个虚函数。实际上,因为所有动物都没有默认声音,所以您不知道该作为默认行为,因此您将此成员函数声明为纯虚函数。
然后告诉任何人创建派生类的实例,例如Lion类,他们必须提供makeSound成员函数的实现,它将以某种方式提供咆哮。
编辑:我忘了补充一点,这是Scott Meyers的优秀着作“Effective C ++”(sanitised Amazon link)中的一篇文章,我强烈推荐。
HTH
欢呼声,
答案 1 :(得分:3)
基本上,当您想要创建一个可以处理多种类的解决方案的泛型类时,无需为要支持的所有类创建父类。
你可以给出你想要使用的类(最好的例子是一个容器,它可以存储你随创建传递的任何类型)
//----- the container
template <class T>
class Stack
{
public:
T* stackPtr;
}
//----- example
void main()
{
typedef Stack<float> FloatStack;
typedef Stack<int> IntStack;
}
现在,您可以使用相同的类存储浮点数和整数,而无需为每种类型编写特定的类。
答案 2 :(得分:2)
简短回答:如果没有用:不要。 如果它似乎解决了一个问题(不同类型的代码重用,......),首先在没有模板的情况下实现和调试,然后添加模板参数。
在STL / boost之前,他们很高兴制作容器。
答案 3 :(得分:2)
在您提供的示例中,只要为您要比较的实例的数据类型定义了operator >
,一切都会正常。
例如,如果您定义以下类:
class fraction
{
private:
int _num, _den;
public:
fraction(int num, int den)
{
if (den >= 0)
{
_num = num;
_den = den;
}
else
{
_num = -num;
_den = -den;
}
}
fraction(const fraction &f)
{
_num = f._num;
_den = f._den;
}
bool operator > (const fraction &f) const
{
return (_num * f._den) > (f._num * _den);
}
bool operator == (const fraction &f) const
{
return (_num * f._den) == (f._num * _den);
}
};
然后,您可以将模板函数用于此类的实例。
int main(int argc, char* argv[])
{
fraction a(1,2); // 0.5
fraction b(3,4); // 0.75
assert(GetMax/*<fraction>*/(a,b) == a);
return 0;
}
答案 4 :(得分:1)
当您需要参数化由类表示的概念时。
例如,如果您有一个表示管理对象类型的方法的类
class MyThingManager
{
void add( MyThing& mything );
//...
};
...也许您稍后需要在新类型中使用完全相同的行为,但管理不同的类型。然后你可以选择使用复制/粘贴/替换 - 这将导致你的脚立即打开地狱 - 或者让你的类有一个类型来管理作为参数:
template< class ThingType >
class ThingManager
{
void add( ThingType& thing );
//...
};
这样你就不会重复代码了。
另一个问题是当您希望某些函数调用与具有所需语义的任何参数兼容时:
template< class MyType >
void addPi( MyType& value )
{
value += PI;
}
这样你(再次)不必为参数中的每种类型重复代码。
它没有被称为“通用编程”。
这些是简单的情况,但是当您想要进行一些元编程时,会出现更复杂的情况。如果你想去那里,请在编写地狱代码之前阅读至少一本书。我推荐“C ++模板元编程”和优秀的“现代C ++设计”一书,用于更高级的模板使用,如政策模式和其他众所周知的。
答案 5 :(得分:1)
回答第二个问题( 但我们想传递一个对象(自定义类)进行比较,我们如何实现?)
如果你想在模板函数中使用你自己的类 运算符&gt;。 您的类只需要定义此运算符或函数。
重要的是你的班级需要定义相同的内容 模板使用的运算符或函数。
/托拜厄斯
答案 6 :(得分:1)
template <class myType>
const myType& GetMax (const myType& a, const myType& b) {
return (a<b?b:a);
}
template <class myType, class Compare>
const myType& GetMax (const myType& a, const myType& b, Compare compare) {
return (compare(a,b)?b:a);
}
Samle用法:比较C风格的字符串:
bool c_strings_less(const char* a, const char* b)
{
return std::strcmp(a, b) < 0; //is a less than b
}
const char* greater = GetMax("hello", "world", c_strings_less);
这是std :: max算法的工作原理。 (我也做了一些修改,例如在C ++中习惯用谓词定义“小于”比较。)
或者如果你问过,GetMax如何适用于任意用户定义的类型,那么那些必须重载运算符&gt;或者你的函数会导致编译错误。
答案 7 :(得分:0)
如果你不知道你的变量的类型,或者你想对许多类型的变量做同样的事情,你可以使用模板......
如果你想添加2个int,你想得到一个int来返回 如果你想加2双,你想得到一个双倍的回报
所以你使用模板......
答案 8 :(得分:0)
模板提供了在KNOWN AT COMPILE TIME数量上进行参数化的方法。请注意,它可以是一个类型(std :: vector只包含整数),但它们也可以是值:
template <int N, typename T > class MyType
在整数和类型上进行模板化,并且MyType&lt; 2,int&gt;将是与MyType&lt; 3,int&gt;不同的类型。
此外,模板允许模板元编程:即编译器在编译时执行程序。 Erwin Unruh在编译时计算素数有一个引人入胜的例子。
查看http://ubiety.uwaterloo.ca/~tveldhui/papers/priority.html了解一小段历史。
答案 9 :(得分:0)
重要的是要注意不写自己的模板是完全正常的。如果您不确定为什么需要它们,那么您可能不需要它们。模板是一个非常强大的工具,但它们并不总是最好的解决方案。
在任何常见的开发平台上,标准库提供了许多旧式传统模板使用的高质量实现。使用标准库类和函数不需要编写新模板。例如,它提供了std :: max(),它与您的示例相同。