上次作业中,已经提到了C++ 11的特性。不过,光说不练假把式,这次作业中,我们就对此进行验证。
进行测试的4个项目分别为:
1.变量作用域和生命周期
2.堆和栈
3.unique_ptr和shared_ptr
4.分割url
为了保证测试的有效性,先要检测编译器对于C++ 11的支持情况。以下是目前主流编译器的支持情况:
由于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.堆和栈
堆和栈是一个容易上新手感到困惑的概念。其实很简单,只要这样理解:栈空间会自动消失,堆空间如果不释放,就不会消失。就类似第一个问题里面,栈的变量都是局部变量,出了那个局部,变量就消失了。比如如下代码:
#includeusing 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()函数里两句话换一下位置以后,再加上点修改,就产生了奇妙的现象:程序能输出一个结果,不过非常诡异。
#includeusing 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#include using namespace std;int main(){ std::unique_ptr p1(new int(5)); std::unique_ptr p2=p1; return 0;}
为了实现这个转移功能,我们就需要修改代码:
#include#include using namespace std;int main(){ std::shared_ptr p1(new int(5)); std::shared_ptr p2 = p1; cout << *p1 << endl; cout << *p2 << endl; return 0;}
move()函数进行了交接操作,p1内存归p2所有,p1变成无效指针,运行起来能输出p1,输出p2就会产生错误
然后就顺利完成了
unique_ptr的意义在于,有效防止了指针悬挂,以及由此引起的非法访问操作。
shared_ptr
感觉这个就像个超级保姆,有效避免了内存泄漏。
#include#include using namespace std;int main(){ std::shared_ptr p1(new int(5)); std::shared_ptr 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#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())!='\n') { if (isChar(c)) { inWord = 1; printf("%c", c); } else if (inWord) { printf(", "); inWord = 0; } } printf("\n"); return 0;}
中文的实现主要在于字符的判断
以上的代码十分简单,但是有个小问题,不支持"://"的判断,不过不要紧,稍加改动就可以:
#include#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())!='\n') { 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("\rNot a valid URL!"); } printf("\n"); return 0;}
经过这样的修改,程序就具有了超强的鲁棒性,完全不怕数据溢出,哪怕几千万个字节的输入也都可以分割。
程序也有了超强的性能,内存占用几乎为0,可以以超高的吞吐量处理流数据
中文的支持,统统能够分割。
简洁的代码
url完整性检查
最后,还有跨平台(Windows和OS X),跨编码集的(GBK与UTF-8)支持:
C++代码(使用STL):
#include#include #include #define isChar(x) (x>='a'&&x<='z'||x>='A'&&x<='Z'||x>='0'&&x<='9'||x=='-'||x<0)using namespace std;vector split(string s){ int i = 0, begin, inWord = 0; vector ret; for (i=0;i v = split(s); if (v.size() > 0) { cout << v[0]; } for (i=1;i
使用了两个类,一个是srting,另一个vector。先判断是否有字串”://”,然后就进行分割操作即可。分割的结果存入STL提供的vector中。