分享
 
 
 

Item 3:使容器里的对象拷贝正确且高效

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Item 3:使容器里的对象拷贝正确且高效

scott meyers 著

刘未鹏 译

容器持有对象,但并非你原先给它的那个。此外,当你从容器中得到一个对象,这个对象并不是原先在容器中的那个(译注:你得到了一个拷贝)。当你调用insert,push_back等操作向容器中插入元素时,进入容器的只是对象的一个拷贝。当你调用front或back从容器取得元素时,你得到的也只是容器中原先那个对象的一个拷贝。拷贝进去(copy in),拷贝出来(copy out),那才是STL的方式。

一旦一个对象被置于了容器之中, 以后它被拷贝将不是什么稀奇事儿。如果你对vector,string,deque插入或擦除元素,典型地,元素会以拷贝的方式被搬移(条款5和条款14)。如果你使用排序算法(条款31):next_permutation,previous_permutation;remove,unique(或者它们的同类算法(条款32));rotate,reverse.etc.,对象将以拷贝的形式被搬移。是的拷贝对象是STL的方式。

或许你想知道拷贝是如何进行的。那很简单——对象用它的“具有拷贝能力”的成员函数来进行拷贝,尤其是它的拷贝构造函数(copy constructor)和拷贝赋值操作符(copy assignment operator)。对于一个用户自定义的类如Widget,这些函数传统上像这样定义:

class Widget{

public:

...

Widget(const Widget&); //copy ctor

Widget& operator=(const Widget&); //copy assignment operator

...

};

如果你不自己定义这些函数,你的编译器将会为你生成它们,而对内建型别(ints,pointers,etc.)的拷贝则是由“位逐次拷贝”来完成(关于拷贝构造函数和拷贝赋值操作符的细节,参见C++的介绍性书籍,在我的《Effective C++》的条款11和条款27集中讨论了这些函数的行为)。

因为有这些拷贝操作,本条款的动机现在已经很明显了——如果你用一个其拷贝动作十分昂贵的对象填满容器,“将对象放到容器中”这样简单的行为可以被证明是一个性能的瓶颈。你在容器内移动对象越频繁,内存的释放分配动作就越频繁。如果你有一个具有非传统意义上的拷贝动作的对象,则将它放到容器中几乎总是会带来一些不快的事情(条款8)。

当然,当存在继承时,拷贝会导致“切割”(slice)。就是说,如果你创建了一个容纳基类对象的容器,然后将派生类对象放进去——切割就产生了,对象的继承部分(即比基类对象多出来的部分)会经由基类对象的拷贝构造函数被切割掉。

vector<Widget> vw;

class SpecialWidget:public Widget //SpecialWidget inherits from

{...}; //Widget above

SpecialWidget sw;

vw.push_back(sw); //sw is copied as a base class object

//into vw .its specialness is lost

//during the copying

切割问题说明将派生类对象放入基类对象的容器几乎总是个错误。在这种情况下,如果你想要拷贝后的结果对象在行为上像个派生类对象(如:调用派生类的虚拟函数),则你总会得到错误的结果(更多关于切割问题的幕后信息请参见Effective C++条款22,本书的条款38有关于此问题的另一个例子)。

有一个能使拷贝高效,正确而且对切割问题“免疫”的简单方法,那就是在容器里存放指针(而不是对象)。也就是说,不去创建Widget的容器,而去创建Widget*的容器。拷贝指针是很快的,而且它总会以你期望的方式进行(对指针进行位拷贝)。拷贝一个指针时不会有切割现象发生(译注:因为指针从本质上只是一个存放地址的变量)。不幸的是,指针的容器也有与STL相关的令人头痛之处。你可以从条款7和33了解它们。当你试图寻求能够在避开效率,正确性,和切割问题的同时避免这种头痛的方法时,你会发现智能指针(smart pointer)是一个有吸引力的选择(条款7)。

如果所有这些听起来就像STL是copy-crazy的,请再想一想。是的,STL作了很多拷贝动作,但通常它是为避免不必要的拷贝而设计的。事实上,是为了避免不必要的对象构造。考虑使用C或C++的内建容器(array)的代码:

Widget w[maxNumWidget]; //create an array of maxNumWidgets

//Widgets,default-ctoring each one

以上的代码将构造maxNumWidget个对象,即使通常我们只打算使用其中的一部分,或者我们希望立即用我们从别处得来的信息(如:从文件读入)来改写被默认构造的对象。我们可以使用STL来替代array——我们可以使用vector,它只在需要的时候才增长:

vector<Widget> vw; //create a vector with zero Widget

//objects that will expand as needed

我们也可以创建一个保留了足够空间的空的vector,在其中没有Widget被真正构造(直到需要时):

vector<Widget> vw;

vw.reserve(maxNumWidgets); //see Item 14 for details on reserve

与array相比较,STL容器要“有礼貌”得多,它们只创建(通过拷贝)你要求它们创建的那么多对象。它们只在你指示它们的时候去做。只有当你明确指明它们应该使用默认构造函数的时候它们才会那么做。是的,STL容器拷贝对象,并且,你得理解它们这种行为的意义。但是请不要忘记它们只是array的高阶抽象。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有