上次作业中,已经提到了C++ 11的特性。不过,光说不练假把式,这次作业中,我们就对此进行验证。
进行测试的4个项目分别为:
1.变量作用域和生命周期
2.堆和栈
3.unique_ptr和shared_ptr
4.分割url
为了保证测试的有效性,先要检测编译器对于C++ 11的支持情况。以下是目前主流编译器的支持情况:
http://developer.51cto.com/art/201303/384630.htm
由于Clang编译器对于C++ 11的支持比较不错,所以本次我们就选择了Clang作为编译环境
从中我们可以看到,编译器是3.3版本,支持本次我们需要用到的所有特性。
1.变量作用域和生命周期
这个测试主要是让我们理解变量作用域的概念,所以我们可以使用如下代码:
int main() { int v = 1; cout << "In level 1: v=" << v << endl; { int v = 2; cout << "In level 2: v=" << v << endl; } cout << "Back to level 1: v=" << v << endl; return 0; }
main第一层的v只管第一层,第二层的v只管第二层,第二层的v不会影响到第一层的。如图所示:
运行结果如下:
与上述解释相符。
2.堆和栈
堆和栈是一个容易上新手感到困惑的概念。其实很简单,只要这样理解:栈空间会自动消失,堆空间如果不释放,就不会消失。就类似第一个问题里面,栈的变量都是局部变量,出了那个局部,变量就消失了。比如如下代码:
#include <iostream> using namespace std; int *getValueInStack() { int *v; *v=9210; return v; } int *getValueInHeap() { int *v; v = new int; *v=9210; return v; } int main() { cout << "In heap: v=" << *getValueInStack() << endl; cout << "In stack: v=" << *getValueInHeap() << endl; return 0; }
getValueInStack是从栈里面获得一个变量
getValueInHeap是从堆里面获得一个变量
现在的编译器已经十分智能了,帮我们发现了这个错误,并给出了警告。如果我们不管这个警告,继续执行的话,就会产生非法访存的段错误:
但是,当我把main()函数里两句话换一下位置以后,再加上点修改,就产生了奇妙的现象:程序能输出一个结果,不过非常诡异。
#include <iostream> using namespace std; int *getValueInStack() { int *v; *v=9210; cout << "In getValue(): v=" << *v << endl; return v; } int *getValueInHeap() { int *v; v = new int; *v=9210; cout << "In getValue(): v=" << *v << endl; return v; } int main() { cout << "In heap: v=" << *getValueInHeap() << endl; cout << "In stack: v=" << *getValueInStack() << endl; return 0; }
这段代码是在我输入错误的时候打进去的,可是却能神奇的输出结果。对于这个奇妙的现象,我在Windows 8.1(编译环境Visual Studio 2013)和Mac(Clang & LLVM 3.3)下都进行了测试。居然是不一样的,windows下这里会报错。
由于这个错误,我猜测Windows和Mac有着微妙的差别:Windows编译器对参数是从右往左解析的,Mac是从左往右的。所以Mac在执行getValueInStack之前已经输出了"In stack:"字符串,所以getValueInStack返回的地址的空间先前已经被字符串指针申请过了,所以不会报错。而windows是从右向左的,执行函数之前,那片空间还没有被申请,所以就产生了非法访问。
3.unique_ptr和shared_ptr
这个真的是一个激动人心的想法,本来C语言的内存泄漏问题就比较严重,有了这个以后就可以在很大程度上避免这个问题。
先说unique_ptr,这个的意义就在于一块空间只能有一个指针指向它。下面一张图表示了一个空间指针的交接:
unique_ptr是不能直接传值的,比如如下代码在编译时就会直接报错:
#include <iostream> #include <memory> using namespace std; int main() { std::unique_ptr<int>p1(new int(5)); std::unique_ptr<int>p2=p1; return 0; }
为了实现这个转移功能,我们就需要修改代码:
#include <iostream> #include <memory> using namespace std; int main() { std::shared_ptr<int>p1(new int(5)); std::shared_ptr<int>p2 = p1; cout << *p1 << endl; cout << *p2 << endl; return 0; }
move()函数进行了交接操作,p1内存归p2所有,p1变成无效指针,运行起来能输出p1,输出p2就会产生错误
然后就顺利完成了
unique_ptr的意义在于,有效防止了指针悬挂,以及由此引起的非法访问操作。
shared_ptr
感觉这个就像个超级保姆,有效避免了内存泄漏。
#include <iostream> #include <memory> using namespace std; int main() { std::shared_ptr<int>p1(new int(5)); std::shared_ptr<int>p2 = p1; cout << *p1 << endl; cout << *p2 << endl; return 0; }
shared_ptr的意义在于,统一管理了指针的分配,在没有指针指向空间的时候立即释放空间,很大程度上避免了内存泄漏。
4.url分割
要求:
1. 类的定义和使用,基本成员是否完整
2. 输入参数的检查及其他鲁棒性的考虑
3. STL和C++11元素的使用
4. 除http://之外, 是否有考虑ftp:// site:// 等情况
5. 是否考虑url中的中文
6. 算法是否简洁高效
7. 代码风格
C语言实现
我的代码做到了
绝对robust,保证不会有溢出,哪怕100W的字符进来也不会
对于所有符号通吃
url中文也毫无压力
算法是O(1)复杂度
代码风格规范
#include <stdio.h> #define isChar(x) (x>='a'&&x<='z'||x>='A'&&x<='Z'||x>='0'&&x<='9'||x=='-'||x<0) int main() { char c; int inWord = 1; while ((c=getchar())!=' ') { if (isChar(c)) { inWord = 1; printf("%c", c); } else if (inWord) { printf(", "); inWord = 0; } } printf(" "); return 0; }
中文的实现主要在于字符的判断
以上的代码十分简单,但是有个小问题,不支持"://"的判断,不过不要紧,稍加改动就可以:
#include <stdio.h> #define isChar(x) (x>='a'&&x<='z'||x>='A'&&x<='Z'||x>='0'&&x<='9'||x=='-'||x<0) int main() { char c; int inWord = 1, readHead = 0; while ((c=getchar())!=' ') { if (isChar(c)) { inWord = 1; printf("%c", c); } else if (inWord) { printf(", "); if (!readHead) { if (c != ':') break; if (getchar() != '/') break; if (getchar() != '/') break; readHead = 1; } else { inWord = 0; } } } if (!readHead) { printf(" Not a valid URL!"); } printf(" "); return 0; }
经过这样的修改,程序就具有了超强的鲁棒性,完全不怕数据溢出,哪怕几千万个字节的输入也都可以分割。
程序也有了超强的性能,内存占用几乎为0,可以以超高的吞吐量处理流数据
中文的支持,统统能够分割。
简洁的代码
url完整性检查
最后,还有跨平台(Windows和OS X),跨编码集的(GBK与UTF-8)支持:
C++代码(使用STL):
#include <iostream> #include <string> #include <vector> #define isChar(x) (x>='a'&&x<='z'||x>='A'&&x<='Z'||x>='0'&&x<='9'||x=='-'||x<0) using namespace std; vector<string> split(string s) { int i = 0, begin, inWord = 0; vector<string> ret; for (i=0;i<s.size();i++) { if (!inWord && isChar(s[i])) { begin = i; inWord = 1; } else if (inWord && !isChar(s[i])) { ret.push_back(s.substr(begin, i-begin)); inWord = 0; } } return ret; } int main() { string s; int i; getline(cin, s); s = s+' '; if (s.find("://") == string::npos) { cout << "not a url" << endl; return 0; } vector<string> v = split(s); if (v.size() > 0) { cout << v[0]; } for (i=1;i<v.size();i++) { cout << ", " << v[i]; } cout << endl; return 0; }
使用了两个类,一个是srting,另一个vector。先判断是否有字串”://”,然后就进行分割操作即可。分割的结果存入STL提供的vector中。