侃一侃vc的std::string
来源:互联网 发布:隐秘录像 知乎 编辑:程序博客网 时间:2024/06/05 19:01
那天心血来潮敲下了这坨代码:
一,结构布局
string的原型是
它是basic_string一个typedef,首先来看basic_string的类层次结构:
_Container_base_secure
/\
|
_String_base
/\
|
_String_val
/\
|
basic_string
_Container_base_secure里有一个_Iterator_base类型的指针成员_Myfirstiter,得占4个字节,_String_base没有数据成员,_String_val里有一个_Alval类型(一个allocator)的成员_Alty,没有数据成员,只占用1个字节,加上padding,也就是4个字节,basic_string有3个数据成员,一个union占16个字节,两个跟长度相关的整型变量各4个字节。所有类都不含虚函数,布局如下:
这个是basic_string的union:
下面是一段测试拷贝不同字符串长度的代码:
buffer size: 4, loop: 1000000, used 78 ms
buffer size: 8, loop: 1000000, used 94 ms
buffer size: 12, loop: 1000000, used 93 ms
buffer size: 16, loop: 1000000, used 94 ms
buffer size: 20, loop: 1000000, used 422 ms
buffer size: 24, loop: 1000000, used 422 ms
buffer size: 32, loop: 1000000, used 438 ms
在字符串(包含0结尾字符)小于16个字节的时候消耗的时间都很低而且几乎一致(94ms),而一旦超过了16个字节消耗时间则迅速增加,变成了422ms(近5倍)。可以看出,如果使用长度较小的字符串,在涉及大量拷贝处理的时候,vc这个技巧的效率还是挺高的。
下面是相同代码采用使用了Copy-On-Write技术的GNU std::string的运行结果(Ubuntu + gcc4.4 + -O2参数):
buffer size: 8, loop: 1000000, used 140 ms
buffer size: 12, loop: 1000000, used 119 ms
buffer size: 16, loop: 1000000, used 140 ms
buffer size: 20, loop: 1000000, used 126 ms
buffer size: 24, loop: 1000000, used 154 ms
buffer size: 32, loop: 1000000, used 129 ms
速度增长很平均,在16字节以下的时候用时比vc多,但是超过16字节后效率比vc高(细节没去研究)。
二,虚析构函数
basic_string和它的基类_Container_base_secure的析构函数都不是virutual function,这在继承的时候会有副作用,先看下面这段代码:
没有虚函数所以析构函数无法动态绑定,上面的代码运行到delete时将直接调用std::string的析构函数,而不会去找mystring的析构函数。如果~mystring涉及到资源释放的话,那么无疑上面的代码将导致泄漏。
还有一点值得一提的就是对std::string或包含std::string的对象实施ZeroMemory
虽然大多数合格的C++程序员都知道不应该对非POD对象实施ZeroMomory,但是在工程实际中确实有这样的代码,而且他们运行的很正常(vc++下),这是为什么呢?我试图做一个简单解释。
如上所述,std::string的继承体系里没有virtual function,对象布局里也就没有vptr,而且整个继承体系都采用的是单继承,bptr又省了(参考Lippman的<Inside C++ Object Model>),所以std::string对象的布局全是它的自身的数据成员,_Container_base_secure的_Myfirstiter是指针成员,本身在初始化时就需要置零,_String_val的_Alval成员是个allocator,只有函数不包含数据,调用时也就不必传this指针,所以给它置零不会有副作用,basic_string指针本身的三个成员分别是缓冲区,字符串长度和预留长度,初始化时将它们置零也是没有副作用的。所以对std::string进行ZeroMemory操作的代码依然运行的很正常。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->cout << sizeof(std::string) << endl;
我的平台是XP+VC9.0,运行结果是32,不知道为什么要那么多,于是在源代码里捣鼓了一番,许久之后终于有了一点眉目,下面是我的一些总结。Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->cout << sizeof(std::string) << endl;
一,结构布局
string的原型是
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->typedef basic_string<char, char_traits<char>, allocator<char> > string;
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->typedef basic_string<char, char_traits<char>, allocator<char> > string;
它是basic_string一个typedef,首先来看basic_string的类层次结构:
_Container_base_secure
/\
|
_String_base
/\
|
_String_val
/\
|
basic_string
_Container_base_secure里有一个_Iterator_base类型的指针成员_Myfirstiter,得占4个字节,_String_base没有数据成员,_String_val里有一个_Alval类型(一个allocator)的成员_Alty,没有数据成员,只占用1个字节,加上padding,也就是4个字节,basic_string有3个数据成员,一个union占16个字节,两个跟长度相关的整型变量各4个字节。所有类都不含虚函数,布局如下:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->_Container_base_secure
_Myfirstiter:_Iterator_base* (4 bytes)
_String_base (nop)
_String_val
_Alty:_Alval (1 byte +3 bytes padding)
basic_string
_Bx : _Bxty (16 bytes)
_Mysize : size_type (4 bytes)
_Myres : size_type (4 bytes)
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->_Container_base_secure
_Myfirstiter:_Iterator_base* (4 bytes)
_String_base (nop)
_String_val
_Alty:_Alval (1 byte +3 bytes padding)
basic_string
_Bx : _Bxty (16 bytes)
_Mysize : size_type (4 bytes)
_Myres : size_type (4 bytes)
这个是basic_string的union:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->enum{ _BUF_SIZE = 16 / sizeof (_Elem) < 1 ? 1 : 16 / sizeof(_Elem)};
union _Bxty
{
_Elem _Buf[_BUF_SIZE];
_Elem* _Ptr;
} _Bx;
通过这个union可以看出vc的std::string在字符串长度较小的时候会使用一个栈上缓冲区(_Bxty::_Buf)来保存字符串内容,如果字符串长度超过了某个范围则会使用allocator分配动态内存(_Bxty::_Ptr),_BUF_SIZE控制着这个范围值,_Buf缓冲区始终是16个字节大小。可以看出,vc的std::string没有使用通用Copy-On-Write技术,因为它没有reference count成员。那么vc采用的这个技术对于字符串的使用效率在实际使用中表现如何呢?Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->enum{ _BUF_SIZE = 16 / sizeof (_Elem) < 1 ? 1 : 16 / sizeof(_Elem)};
union _Bxty
{
_Elem _Buf[_BUF_SIZE];
_Elem* _Ptr;
} _Bx;
下面是一段测试拷贝不同字符串长度的代码:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
template <size_t N>
void test_string()
{
const size_t loop = 1000000; // 一百万次
// 预先分配好内存
vector<string> vec;
vec.resize(loop);
char szbuf[N] = {};
memset(szbuf, 'a', N - 1);
// 计算拷贝时间
size_t time = GetTickCount();
for (int i = 0; i < loop; ++i)
{
vec[i] = szbuf;
}
time = GetTickCount() - time;
printf("buffer size: %u, loop: %u, used %u ms\n", N, loop, time);
}
int main(int argc, char* argv[])
{
test_string<8>();
test_string<12>();
test_string<16>();
test_string<20>();
test_string<24>();
test_string<32>();
return 0;
}
编译使用release版全默认参数,结果如下:Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
template <size_t N>
void test_string()
{
const size_t loop = 1000000; // 一百万次
// 预先分配好内存
vector<string> vec;
vec.resize(loop);
char szbuf[N] = {};
memset(szbuf, 'a', N - 1);
// 计算拷贝时间
size_t time = GetTickCount();
for (int i = 0; i < loop; ++i)
{
vec[i] = szbuf;
}
time = GetTickCount() - time;
printf("buffer size: %u, loop: %u, used %u ms\n", N, loop, time);
}
int main(int argc, char* argv[])
{
test_string<8>();
test_string<12>();
test_string<16>();
test_string<20>();
test_string<24>();
test_string<32>();
return 0;
}
buffer size: 4, loop: 1000000, used 78 ms
buffer size: 8, loop: 1000000, used 94 ms
buffer size: 12, loop: 1000000, used 93 ms
buffer size: 16, loop: 1000000, used 94 ms
buffer size: 20, loop: 1000000, used 422 ms
buffer size: 24, loop: 1000000, used 422 ms
buffer size: 32, loop: 1000000, used 438 ms
在字符串(包含0结尾字符)小于16个字节的时候消耗的时间都很低而且几乎一致(94ms),而一旦超过了16个字节消耗时间则迅速增加,变成了422ms(近5倍)。可以看出,如果使用长度较小的字符串,在涉及大量拷贝处理的时候,vc这个技巧的效率还是挺高的。
下面是相同代码采用使用了Copy-On-Write技术的GNU std::string的运行结果(Ubuntu + gcc4.4 + -O2参数):
buffer size: 8, loop: 1000000, used 140 ms
buffer size: 12, loop: 1000000, used 119 ms
buffer size: 16, loop: 1000000, used 140 ms
buffer size: 20, loop: 1000000, used 126 ms
buffer size: 24, loop: 1000000, used 154 ms
buffer size: 32, loop: 1000000, used 129 ms
速度增长很平均,在16字节以下的时候用时比vc多,但是超过16字节后效率比vc高(细节没去研究)。
二,虚析构函数
basic_string和它的基类_Container_base_secure的析构函数都不是virutual function,这在继承的时候会有副作用,先看下面这段代码:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->class mystring : public std::string
{
public:
~mystring()
{
cout << "dctor" << endl;
}
};
void main()
{
std::string* pstr = new mystring();
delete pstr; // ~mystring()没有被调用
}
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->class mystring : public std::string
{
public:
~mystring()
{
cout << "dctor" << endl;
}
};
void main()
{
std::string* pstr = new mystring();
delete pstr; // ~mystring()没有被调用
}
没有虚函数所以析构函数无法动态绑定,上面的代码运行到delete时将直接调用std::string的析构函数,而不会去找mystring的析构函数。如果~mystring涉及到资源释放的话,那么无疑上面的代码将导致泄漏。
还有一点值得一提的就是对std::string或包含std::string的对象实施ZeroMemory
虽然大多数合格的C++程序员都知道不应该对非POD对象实施ZeroMomory,但是在工程实际中确实有这样的代码,而且他们运行的很正常(vc++下),这是为什么呢?我试图做一个简单解释。
如上所述,std::string的继承体系里没有virtual function,对象布局里也就没有vptr,而且整个继承体系都采用的是单继承,bptr又省了(参考Lippman的<Inside C++ Object Model>),所以std::string对象的布局全是它的自身的数据成员,_Container_base_secure的_Myfirstiter是指针成员,本身在初始化时就需要置零,_String_val的_Alval成员是个allocator,只有函数不包含数据,调用时也就不必传this指针,所以给它置零不会有副作用,basic_string指针本身的三个成员分别是缓冲区,字符串长度和预留长度,初始化时将它们置零也是没有副作用的。所以对std::string进行ZeroMemory操作的代码依然运行的很正常。
- 侃一侃vc的std::string
- 侃一侃vc的std::string (转)
- VC++2005中CString到std::string的转换
- VC中STL std::string类的使用
- VC中STL std::string类的使用 .
- VC开发中CString,std::string的错误使用
- std::string的用法
- 全局的std::string
- std::string的用法
- std::string的用法 .
- vc++ .net std::string is not a member of std
- 【VC++】CString与std::string互转
- std::vector到std::string的转换
- std::string、std::wstring的关系
- vc++6.0STL中std::string类导致程序崩溃的解决方案
- std::string的基本用法
- std::string的工具函数
- std::string的工具函数
- Socket编程中的select多路复用
- ASP.NET 安全策略学习与 .NET 测试环境搭建
- Using the Flex Compilers Flex编译器的使用 第三部分
- Option
- wordpress代码调用大全
- 侃一侃vc的std::string
- HDU 2955
- C#之HelloWorld,防止黑屏一闪而过
- android webview goBack不起作用的解决方法
- Hadoop回收站trash-恢复删除的文件
- 版本压缩(Compress)提示"ORA-00060: deadlock detected while waiting for resource".
- Java堆内存的10个要点
- VisualDSP++ 5.0 无法创建LDF文件
- QTP10.0破解