C++模板概念与基于范围循环(range-based for loop)的陷阱
有些人提到C 模板就会下意识地觉得可怕、看不懂、避而远之。其实模板并不复杂,而且熟练后可以用在日常工作中,可以帮助我们重用代码,让代码更简洁、易读、可维护。
希望这个系列的文章,能够让更多人发现模板的魅力,帮助大家写出更高质量的代码。
什么是模板?
顾名思义,模板就是编译器生成代码用的模子。模板有两类,函数模板和类模板(C 14开始出现了变量模板,不过不在此讨论)。如果想要生成函数代码,则需要用函数模板,如果想要生成类定义,则需要用到类模板。
我们为什么需要模板?
我们有时候会遇到这样的情况:同样的函数,我们要为不同的类型写不同的版本,内容与逻辑都是一摸一样的,只有他们的类型不一样。比如我们写一个max函数,传入两个数字,返回大的数字。
很自然地,两个参数的类型和返回的类型必须是相同的。如果不使用模板,我们需要使用函数重载,为不同的类型写不同的函数:
int max(int a, int b) {
return a < b ? b: a;
}
float max(float a, float b) {
return a < b ? b: a;
}
这里我只写了2个函数,实际上short, long, unsigned, double等等类型都需要专门的max函数,结果就是需要写十几个几乎一摸一样的代码。
如果函数功能更复杂一些,函数实现需要更多行,就会出现大量冗余重复的代码,而且不容易维护,很容易出错。这个时候如果我们能够根据特定的模板批量生成一系列代码,将会方便很;为此,我们可以使用C 中的模板。
C 基于范围循环(range-based for loop)的陷阱:
C 的基于范围的循环是C 11出现的新特性,很方便,一定程度上替代了使用迭代器的for循环用法。不过基于范围的for循环有一个隐藏的陷阱,如果不注意可能会出现严重的内存错误。
看下面这个代码:
1#include < iostream > 2#include < string > 3 4 using namespace std;
5 6 struct MyClass 7 {
8 string text = "MyClass";
9 10 string & getText() 11 {
12
return text;
13
}
14
};
15 16 int main() 17 {
18
for (auto ch: MyClass().text) 19 {
20 cout << ch;
21
}
22 cout << endl;
23
}
这个代码很简单,输出结果就是 "MyClass"。但如果稍微修改第18行,改为以下的样子:
for (auto ch: MyClass().getText()) {
cout << ch;
}
结果什么都不会输出,程序直接退出。要理解为什么会出现这种行为,要先知道基于范围的for循环是怎么定义的。
基于范围的for循环定义:
在C 11标准中,它有以下的格式
1 attr(optional) for (range_declaration: range_expression) loop_statement
其中attr是可选的,range_declaration部分相当于我们代码中的 "auto ch",range_expression部分相当于 "MyClass().getText()",loop_statement就是 "{ cout << ch; }"
标准规定,上面的循环表达式应当等价于
{
auto && __range = range_expression;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; __begin) {
range_declaration = *__begin;
loop_statement
}
}
转载声明:本文来源于网络,不作任何商业用途。
全部评论
暂无留言,赶紧抢占沙发