(1) 理解C++变量的作用域和生命周期
作用域是空间上的概念,包含全局作用域、文件作用域和局部作用域。生命周期是时间上的概念,其内存分配位置不同,可被引用的时间段也不同。static关键字和{}等标识符可以用来指定变量的作用域和生命周期。
局部变量,分配内存是分配在栈存储区上的,其作用域也只是在局部函数内,在定义该变量的函数内,只要出了该函数,该局部变量就不再起作用,该变量的生命周期也只是和该函数同在。
局部静态变量,分配的内存也是在静态存储内存上的,其第一次初始化后就一直存在直到程序结束,该变量的特点是其作用域只在定义它的函数内可见,出了该函数就不可见了。
1 int function1(int n){ 2 static int i = 0; //静态局部变量生命周期为程序运行期 3 return i++; // i只初始化一次,每次返回值递增 4 } 5 int function2(){ 6 int i = 0; //OK, 上述i作用域之外 7 return i++; //每次返回值相同,因为生命周期外被销毁,每次重新初始化。 8 }
(2) 理解堆和栈,两种内存的申请和释放的方式
堆和栈是内存分配方式之二,除此之外还有全局变量与静态变量区、代码区等等。
栈,由编译器自动分配和释放,主要是函数体的地址,参数和局部变量,静态变量不包含其中,操作方式类似于数据结构中的栈。
堆,由程序员手动完成申请和释放的,需要通过malloc和new手动申请,使用后使用delete手动释放。程序员没有手动释放的话,当程序结束时由系统释放没有释放的空间,其实现方式与数据结构中的堆完全不同,此时的堆的实现方式有些类似于数据结构中的链表。
1 main(){ 2 int b; //栈 3 char s[] = "abc"; //栈 4 char *p2; //栈 5 char *p3 = "123456"; // 123456\0在常量区,p3在栈上。 6 static int c =0; //全局(静态)初始化区 7 dog Wangwang = new dog("Wangwang"); // Wangwang狗人工申请,在堆上 8 p1 = (char *)malloc(10); //堆,手动申请手动释放 9 p2 = (char *)malloc(20); //自主确定大小 10 strcpy(p1, "123456"); 11 // 123456\0放在常量区,编译器可能会将它与p3所指的"123456"优化成一个地方。 12 }
补充:
栈,只要栈剩余的空间大小比申请的空间小,系统就自动为其分配空间,否则就会报错说明栈空间溢出。
堆,首先要知道操作系统中有一个存放空闲存储块的链表,当程序员申请空间的时候,系统就会遍历整个链表,找到第一个比申请空间大的空闲块节点,系统会将该空闲块从空闲链表中删除,分配给程序,同时系统会记录这个空闲块的首地址和申请的大小,当程序员使用delete释放该空间的时候能够找到该存储区。另外,申请的空间不一定与找到的空闲块大小相同,多出来剩余的空闲区会被系统重新添加到空闲链表中。
栈,是一种向低地址扩展的数据结构,并且是连续的存储空间,所以栈顶和栈的最大容量是固定的,在windows下,栈的最大容量是2m或者是1m,是在编译的时候就已经确定的,当申请空间大于栈的剩余空间的时候,就会报错说明overflow,所以栈能够申请的空间是比较有限的。
堆,是一种向高地址扩展的数据结构,并且是不连续的,因为系统采用的是链表的方式存放空闲存储块,当然是不连续的,链表的遍历方向是由低向高的,所以堆能够申请的空间的大小其实等同于整个系统的虚拟内存,只要还有内存空间,那么堆就能够不受限制的申请空间,这种方式比较灵活,申请空间也较大。
(3) 理解unique_ptr和shared_ptr
两者均是C++11的智能指针。unique_ptr只可以被move,并且原unique_ptr失效,不可被传值、取值等任何或进行任何需要复制的算法和操作。这个限制防止了很多冲突。
shared_ptr可以被复制给其他shared_ptr, 可以在函数调用中作为参数进行传递,其根本是允许了多个指针指向同一个目标,同一个目标可以被多个指针操作。当指向该目标的有效指针减为0,则目标被自动释放。
个人的理解是,unique_ptr防止了指针操作的冲突,而shared_ptr防止了 memory leak, 程序员不必纠结于内存释放。但是shared+ptr也有低效率的劣势,并存在互环问题。
(4) 尝试用“C++0x”,“C++11 & STL”两种不同的代码风格分割一个url
c++0x风格,使用strtok()分割。
1 #include <stdio> 2 #include <string> 3 int main(){ 4 char s[50]; 5 const char *div = "/.:"; 6 gets(s); 7 char *tokenPtr; 8 tokenPtr = strtok(s,div); 9 while(tokenPtr != NULL){ 10 printf("%s,",tokenPtr); 11 tokenPtr=strtok(NULL,div); 12 } 13 return 0; 14 }
C++和STL,我使用STL的容器来处理。
1 #include<vector> 2 using namespace std; 3 4 vector<string> split(string s, string div){ //输入串和分割字符串 5 string::size_type idx; 6 vector<string> out; 7 for(int i=0; i < s.size(); i++){ 8 idx = s.find(div, i); //查找分割位置 9 if(idx < size){ 10 string str=s.substr(i, idx - i);//切割出子串 11 out.push_back(str + ","); //拼到输出串 12 i = idx + div.size()-1; 13 } 14 } 15 return out; 16 }
不过,这个用C#的Split()函数更方便。鲁棒性可以通过将split放到try{}catch{}中保证。
1. 这里面能够处理site:// http:// https://等开头,也可以处理中文(必要时可改变字符串编码)。
2. 还需要的就是鲁棒性不够,因为目前还没对输入进行检查。两种风格的代码,都可以预先用下面的正则表达式匹配检查是不是url:
const regex pattern("^((http|https|site|ftp|ftps)\://)?(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/?)([a-zA-Z0-9\u4e00-\u9fa5\-\.\?\,\'\/\\\+&%\$#_]*)?$");