大部分人使用vector构造字符串数组,都是用vector<string>,但是也不乏人使用vector<char*> ,用vctor<char*>不会遇到任何问题,但是既然用了C的东西,很多东西需要自己来控制,不像C++的string那样: std::string is a string class, encapsulating all the required data that makes up a string, along with allocation and deallocation functionality.
可以看下面的这段代码:
1 int main(int argc, const char *argv[])
2 {
3 vector<char*> vctTemp;
4 char* pchA = (char*)malloc(5);
5 char* pchB = (char*)malloc(5);
6 memcpy(pchA, "hello", 5);
7 memcpy(pchB, "world", 5);
8 vctTemp.push_back(pchA);
9 vctTemp.push_back(pchB);
10 vector<char*>::iterator iter;
11 for (iter=vctTemp.begin(); iter!=vctTemp.end(); iter++) {
12 cout<<*iter<<endl;
13 }
14 return 0;
15 }
使用valgrind来check下内存泄漏情况.
==3317== LEAK SUMMARY: ==3317== definitely lost: 10 bytes in 2 blocks ==3317== indirectly lost: 0 bytes in 0 blocks ==3317== possibly lost: 0 bytes in 0 blocks ==3317== still reachable: 0 bytes in 0 blocks ==3317== suppressed: 0 bytes in 0 blocks
很明显内存泄漏了10Bytes . 只能通过手动free .
上面的代码可以改成:
1 for (iter=vctTemp.begin(); iter!=vctTemp.end(); iter++) {
2 cout<<*iter<<endl;
3 free(*iter); //手动进行free
4 }
再来check下
==3343== HEAP SUMMARY: ==3343== in use at exit: 0 bytes in 0 blocks ==3343== total heap usage: 4 allocs, 4 frees, 22 bytes allocated
显然string类更好,该不需要我们做的,都帮我们做了,但是如果你很注意性能的话清使用C-style的字符串. 有人会说string效率比C-Style高(是的在某方面,但是C完全可以避免, 因为C++也是C写的, 只是加了一层数据结构的封装,网上的string效率比C风格代码高的代码有些有问题的.或者说不公平的测试代码)
比如(网上很多bbs' target='_blank'>论坛上不公平的测试代码):
C++ string:
3 int main(int argc, const char *argv[])
4 {
5 INIT_TIME;
6 START_TIME;
7 string str("a very larg literal string");
8 int i;
9 int errors = 0;
10 for (i = 0; i < 10000000; i++) {
11 int len = str.size();
12 string str2 = str;
13 if (str != str2) {
14 ++errors;
15 }
16 }
17 STOP_TIME;
18 PRINT_TIME;
19 return 0;
20 }
C-Style:
2 int main(int argc, const char *argv[])
3 {
4 INIT_TIME;
5 START_TIME;
6 int i ;
7 char* pc = "a very larg literal string";
8 int errors = 0;
9 for (i = 0; i < 10000000; i++) {
10 int len = strlen(pc);
11 char* p2 = malloc(len+1);
12 strcpy(p2, pc);
13 if (strcmp(p2, pc)) {
14 ++errors;
15 }
16 free(p2);
17 }
18 STOP_TIME;
19 PRINT_TIME;
20 return 0;
21 }
测试结果为: string的时间比C-style快2倍.
让我们来看下不公平的C代码:
2 int main(int argc, const char *argv[])
3 {
4 INIT_TIME;
5 START_TIME;
6 int i ;
7 char* pc = "a very larg literal string";
8 int errors = 0;
9 for (i = 0; i < 10000000; i++) {
10 int len = strlen(pc); //计算长度需要放在for循环内吗? string是显示的记住size()大小的,strlen是需要把字符串遍历一遍!
11 char* p2 = malloc(len+1); //malloc更不需要放在for循环内 如果你说和string进行对比, 我只想说想操作C就需要更好的操作语言的能力,而不是那么傻的放在for循环内.
12 strcpy(p2, pc);
13 if (strcmp(p2, pc)) {
14 ++errors;
15 }
16 free(p2);//这个也不需要放在里面了.
17 }
18 STOP_TIME;
19 PRINT_TIME;
20 return 0;
21 }
改后:
2 int main(int argc, const char *argv[])
3 {
4 INIT_TIME;
5 START_TIME;
6 int i ;
7 char* pc = "a very larg literal string";
8 int errors = 0;
9 int len = strlen(pc);
10 char* p2 = malloc(len+1);
11 for (i = 0; i < 10000000; i++) {
12 strcpy(p2, pc);
13 if (strcmp(p2, pc)) {
14 ++errors;
15 }
16 }
17 free(p2);
18 STOP_TIME;
19 PRINT_TIME;
20 return 0;
21 }
再比下效率,就会知道C-Style的优势了.
好,我们来谈谈C++ 的string的优势: 引用计数 和 COW
引用计数:
3 int main(int argc, const char *argv[])
4 {
5 string str1 = "hello";
6 string str2(str1);
7
8 printf("&str1=%p\n", str1.c_str());
9 printf("&str2=%p\n", str2.c_str());
10
11 return 0;
12 }
Result:
&str1=0x8cb3014 &str2=0x8cb3014
两个字符串地址相同,string拷贝构造函数并非真正的进行字符串拷贝, 只是将字符串的引用值加 1 , str1 和 str2 指向的是同一个字符串.也就是说,上面那个例子中的for循环内, 并没有真实的进行拷贝操作,而是进行了引用计数的递增. 那为什么刚才和C-Style的运行时间差不多? 其实差很多,因为我们的例子中测试的字符串长度太小了, 比如如果是这样的字符串:
char* p = "hell1111111111111111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111111111112222222222222222222 222222222222222222222222222222222222222222222222222222222222222223333333333333333 333333333333333333333333333333333333333333333333333333333333333333333333333333333 333333333333333333333333333333333333333333333333333333333333333333333333333333333 333333333333333333333333333333333333344444444444444444444444444444444444444444444 44444444444444444o";
你就会发现strcpy会很慢了, 而string只要增加引用计数即可.
有人会问, 既然string 字符串拷贝都引用同一地址,那修改某个字符串怎么办呢? 一个修改,不是其它都修改了吗? 当然不是 ,这里C++使用了COW 也就是写时拷贝,
7 string str1 = "hello";
8 string str2(str1);
9 str1[1] = 'w';
10 printf("%p\n", str1.c_str());
11 printf("%p\n", str2.c_str());
7 string str1 = "hello";
8 string str2(str1);
9 char* p = &str1[1];
10 string str3(str1);
11 printf("%p\n", str1.c_str()); //0x8bbf02c
12 printf("%p\n", str3.c_str()); //0x8bbf044
第一段代码中 line 9行 对str1 进行了修改, 你会发现 str1和str2的地址就不一样了. 第二段代码中 str1 和 str3的地址也不一样, 其实如果 我在 13行添加*p = 'H'; 你就会理解了,因为对str1地址的引用,通过也会修改str3, 所以对str1进行修改 或者 使用[] 这个操作符后(C++对[]进行了重载 ), 后面的赋值都是真正的进行拷贝! 而C在拷贝时就已经真正拷贝了.C怎么办呢.只能自己实现引用计数这套算法, 所以 如果单纯的进行拷贝 ,C效率(没有实现引用计数之前)比不过C++ 的string, 而如果你对字符串修改时, string比不过真正的纯C代码了.