`
javatgo
  • 浏览: 1126182 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

标准C++类string的Copy-On-Write技术(三)

阅读更多

2.3.4、 Copy-On-Write的具体实现是怎么样的?

最后的这个问题,我们主要解决的是那个“民主集中”的难题。请先看下面的代码:

string h1 = “hello”;

string h2= h1;

string h3;

h3 = h2;

string w1 = “world”;

string w2(“”);

w2=w1;

很明显,我们要让h1h2h3共享同一块内存,让w1w2共享同一块内存。因为,在h1h2h3中,我们要维护一个引用计数,在w1w2中我们又要维护一个引用计数。

如何使用一个巧妙的方法产生这两个引用计数呢?我们想到了string类的内存是在堆上动态分配的,既然共享内存的各个类指向的是同一个内存区,我们为什么不在这块区上多分配一点空间来存放这个引用计数呢?这样一来,所有共享一块内存区的类都有同样的一个引用计数,而这个变量的地址既然是在共享区上的,那么所有共享这块内存的类都可以访问到,也就知道这块内存的引用者有多少了。

请看下图:

<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><font size="3"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></path><lock v:ext="edit" aspectratio="t"></lock></font></shapetype>

于是,有了这样一个机制,每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造可是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。下面的几个程序片段说明了这两个动作:

//构造函数(分存内存)

string::string(const char* tmp)

{

_Len = strlen(tmp);

_Ptr = new char[_Len+1+1];

strcpy( _Ptr, tmp );

_Ptr[_Len+1]=0; // 设置引用计数

}

//拷贝构造(共享内存)

string::string(const string& str)

{

if (*this != str){

this->_Ptr = str.c_str(); //共享内存

this->_Len = str.szie();

this->_Ptr[_Len+1] ++; //引用计数加一

}

}

//写时才拷贝Copy-On-Write

char& string::operator[](unsigned int idx)

{

if (idx > _Len || _Ptr == 0 ) {

static char nullchar = 0;

return nullchar;

}

_Ptr[_Len+1]--; //引用计数减一

char* tmp = new char[_Len+1+1];

strncpy( tmp, _Ptr, _Len+1);

_Ptr = tmp;

_Ptr[_Len+1]=0; // 设置新的共享内存的引用计数

return _Ptr[idx];

}

//析构函数的一些处理

~string()

{

_Ptr[_Len+1]--; //引用计数减一


// 引用计数为0时,释放内存

if (_Ptr[_Len+1]==0) {

delete[] _Ptr;
}

}

哈哈,整个技术细节完全浮出水面。

不过,这和STLbasic_string的实现细节还有一点点差别,在你打开STL的源码时,你会发现其取引用计数是通过这样的访问:_Ptr[-1],标准库中,把这个引用计数的内存分配在了前面(我给出来的代码是把引用计数分配以了后面,这很不好),分配在前的好处是当string的长度扩展时,只需要在后面扩展其内存,而不需要移动引用计数的内存存放位置,这又节省了一点时间。

STL中的string的内存结构就像我前面画的那个图一样,_Ptr指着是数据区,而RefCnt则在_Ptr-1 或是 _Ptr[-1]处。

2.4、 臭虫Bug

是谁说的“有太阳的地方就会有黑暗”?或许我们中的许多人都很迷信标准的东西,认为其是久经考验,不可能出错的。呵呵,千万不要有这种迷信,因为任何设计再好,编码再好的代码在某一特定的情况下都会有BugSTL同样如此,string类的这个共享内存/写时才拷贝技术也不例外,而且这个Bug或许还会让你的整个程序crash掉!

不信?!那么让我们来看一个测试案例:

假设有一个动态链接库(叫myNet.dllmyNet.so)中有这样一个函数返回的是string类:

string GetIPAddress(string hostname)

{

static string ip;

……

……

return ip;

}

而你的主程序中动态地载入这个动态链接库,并调用其中的这个函数:

main()

{

//载入动态链接库中的函数

hDll = LoadLibraray(…..);

pFun = GetModule(hDll, “GetIPAddress”);

//调用动态链接库中的函数

string ip = (*pFun)(“host1”);

……

……

//释放动态链接库

FreeLibrary(hDll);

……

cout << ip << endl;

}

让我们来看看这段代码,程序以动态方式载入动态链接库中的函数,然后以函数指针的方式调用动态链接库中的函数,并把返回值放在一个string类中,然后释放了这个动态链接库。释放后,输入ip的内容。

根据函数的定义,我们知道函数是“值返回”的,所以,函数返回时,一定会调用拷贝构造函数,又根据string类的内存共享机制,在主程序中变量ip是和函数内部的那个静态string变量共享内存(这块内存区是在动态链接库的地址空间的)。而我们假设在整个主程序中都没有对ip的值进行修改过。那么在当主程序释放了动态链接库后,那个共享的内存区也随之释放。所以,以后对ip的访问,必然做造成内存地址访问非法,造成程序crash。即使你在以后没有使用到ip这个变量,那么在主程序退出时也会发生内存访问异常,因为程序退出时,ip会析构,在析构时就会发生内存访问异常。

内存访问异常,意味着两件事:1)无论你的程序再漂亮,都会因为这个错误变得暗淡无光,你的声誉也会因为这个错误受到损失。2)未来的一段时间,你会被这个系统级错误所煎熬(在C++世界中,找到并排除这种内存错误并不是一件容易的事情)。这是C/C++程序员永远的心头之痛,千里之堤,溃于蚁穴。而如果你不清楚string类的这种特征,在成千上万行代码中找这样一个内存异常,简直就是一场噩梦。

备注:要改正上述的Bug,有很多种方法,这里提供一种仅供参考:

string ip = (*pFun)(“host1”).cstr();

3、 后记

文章到这里也应该结束了,这篇文章的主要有以下几个目的:

1) 向大家介绍一下写时才拷贝/内存共享这种技术。

2) STL中的string类为例,向大家介绍了一种设计模式。

3) C++世界中,无论你的设计怎么精巧,代码怎么稳固,都难以照顾到所有的情况。智能指针更是一个典型的例子,无论你怎么设计,都会有非常严重的BUG

4) C++是一把双刃剑,只有了解了原理,你才能更好的使用C++。否则,必将引火烧身。如果你在设计和使用类库时有一种“玩C++就像玩火,必须千万小心”的感觉,那么你就入门了,等你能把这股“火”控制的得心应手时,那才是学成了。

最后,还是利用这个后序,介绍一下自己。我目前从事于所有Unix平台下的软件研发,主要是做系统级的产品软件研发,对于下一代的计算机革命——网格计算非常地感兴趣,同于对于分布式计算、P2PWeb ServiceJ2EE技术方向也很感兴趣,另外,对于项目实施、团队管理、项目管理也小有心得,希望同样和我战斗在“技术和管理并重”的阵线上的年轻一代,能够和我多多地交流。我的MSN和邮件是:haoel@hotmail.com

我的专栏是:

http://blog.csdn.net/haoel/

<-上一页

(版权所有,转载时请注明作者和出处)

分享到:
评论

相关推荐

    标准C++类string的Copy-On-Write技术

    里,我想从C++类或是设计模式的角度为各位揭开Copy-On-Write技术在string中实现的面纱,以供各位在用C++进行类库设计时做一点参考

    STL 的string类怎么啦

    老实说,我几年前也有同样的痛苦(就是当我写下《标准C++类string的Copy-On-Write技术》之前的一段时间)。那时,我不得不研究那根本不是给人看的SGI出品的string类的源码,代码的可读性几乎为零,而且随着了解越...

    Turbo C++ 3.0[DISK]

    special offer, and write for full details on how to receive a free IntroPak containing a $15 credit toward your first month's on-line charges. 2. Check with your local software dealer or users' ...

    Turbo C++ 3.00[DISK]

    special offer, and write for full details on how to receive a free IntroPak containing a $15 credit toward your first month's on-line charges. 2. Check with your local software dealer or users' ...

    C++MFC教程

    Visual C++MFC入门教程 目录 +-- 第一章 VC入门 |------ 1.1 如何学好VC |------ 1.2 理解Windows消息机制 |------ 1.3 利用Visual C++/MFC开发Windows程序的优势 |------ 1.4 利用MFC进行开发的通用方法介绍 |----...

    Senfore_DragDrop_v4.1

    Earlier versions of Delphi and C++ Builder will not be supported. If you need Delphi 3 or C++ Builder 3 support you will have to revert to version 3.7 of the Drag and Drop Component Suite. The ...

    Google C++ Style Guide(Google C++编程规范)高清PDF

    Classes Doing Work in Constructors Default Constructors Explicit Constructors Copy Constructors Structs vs. Classes Inheritance Multiple Inheritance Interfaces Operator Overloading Access Control ...

    C++中sting类的简单实现方法

    析构函数,赋值运算符重载,运算符+=的重载,运算符[]的重载,c_str(得到一个C风格的字符指针,可操作字符串),Size,Push_Back,Insert(深拷贝),以及用写时拷贝copy_on_write的方式实现基本的String类 深拷贝的方式

    From C to C++

    // convert a string to uppercase! #include #define N 200 int main(){ char ms[N]; int i; printf("Input ms: "); gets(ms); for(i=0;ms[i];i++) if(ms[i]&gt;='a'&&ms[i]) ms[i]-='\x20'; puts(ms); ...

    FastReport.v4.15 for.Delphi.BCB.Full.Source企业版含ClientServer中文修正版支持D4-XE5

    FastReport® VCL is an add-on component that allows your application to generate reports quickly and efficiently. FastReport® provides all the tools necessary for developing reports, including a ...

    acpi控制笔记本风扇转速

    produced when this parameter was a null string (""). Now, the original input filename is used as the AML output filename, with an ".aml" extension. Implemented a generic batch command mode for the ...

    C++大学教程,一本适合初学者的入门教材(part1)

    第1章 计算机与C++编程简介 1.1 简介 1.2 什么是计算机 1.3 计算机组成 1.4 操作系统的变革 I.5 个人计算、分布式计算与客户/a匠务器计算 l. 6 机器语言、汇编语言和高级语言 1.7 C语言与C++的历史 1.8 C++...

    au3反编译源码

    To copy text or to enlarge the log window double click on it. Supported Obfuscators: 'Jos van der Zande AutoIt3 Source Obfuscator v1.0.14 [June 16, 2007]' , 'Jos van der Zande AutoIt3 Source ...

    C++大学教程,一本适合初学者的入门教材(part2)

    第1章 计算机与C++编程简介 1.1 简介 1.2 什么是计算机 1.3 计算机组成 1.4 操作系统的变革 I.5 个人计算、分布式计算与客户/a匠务器计算 l. 6 机器语言、汇编语言和高级语言 1.7 C语言与C++的历史 1.8 C++...

    c/c++函数库说明(api)html版

    所有的 C / C++ 函数 Constructors (cppstring) Constructors (cppvector) Operators (cppbitset) Operators (cppdeque) Operators (cppstack) Operators (cppstring) Operators (cppvector) abort (stdother...

    一个跨平台的CString源码

    // the Standard C++ Library basic_string&lt;&gt; template and add to it the // the following conveniences: // - The full MFC CString set of functions (including implicit cast) // - writing to/reading ...

    FlexGraphics_V_1.79_D4-XE10.2_Downloadly.ir

    on a curve by the distance from the original point on the perimeter. - ADD: Added the method TCustomProp.GetPublishedComplexProp - returns included complex flex-property by name registered through ...

    RxLib控件包内含RxGIF,全部源码及DEMO

    TFormStorage allows you to read and write virtually any component published property to an INI file or the system Registry with virtually no code. Works with 3rd party and your own custom controls as ...

    Linux多线程服务端编程:使用muduo C++网络库

    2.8借shared_ptr 实现copy-on-write. . . . . . . . . . . . . . . . . . . . . . 52 第3章多线程服务器的适用场合与常用编程模型 3.1进程与线程. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . ...

Global site tag (gtag.js) - Google Analytics