Effective STL——容器中所发生的对象拷贝问题

当你从容器中获取一个对象,或者添加一个对象时,你所加入的那个对象和所获取的那个对象都不是你原本期望的那个对象,而是他们的拷贝。
对于容器来说,拷贝是时刻发生的,比如你在vector,string,或者是deque中插入或者删除元素时,现有的元素会发生移动,这将伴随着许多拷贝操作。或是你调用排序算法时,或者rotate,reverse等操作时都会发生大量的拷贝。
你可能会对所有这些拷贝是怎么完成的感兴趣。这很简单,一个对象通过使用它的拷贝成员函数来拷贝,特
别是它的拷贝构造函数和它的拷贝赋值操作符。对于用户自定义类,比如Widget,这些函数传统上是这么声明的:
class Widget {
public:
...
Widget(const Widget&); // 拷贝构造函数
Widget& operator=(const Widget&); // 拷贝赋值操作符
...
};
如果你自己没有声明这些函数,你的编译器始终会为你声明它们。拷贝内建类型(比如int、指针等)也始终
是通过简单地拷贝他们的内在比特来完成的。
也许你可能认为发生拷贝操作很正常,而且并没有什么,但是如果你在容器中使用的是一个拷贝过程很昂贵的对象,那么就连将对象放入容器这么一个简单的操作也可能成为性能的瓶颈。容器中移动越多的东西,你就会在拷贝上浪费越多的内存和时钟周期。此外,如果你有一个非传统意义的“拷贝”的对象,把这样的对象放进容器总是会导致不幸。
当然由于继承的存在,拷贝会导致分割。那就是说,如果你以基类对象建立一个容器,而你试图插入派生类对象,那么当对象(通过基类的拷贝构造函数)拷入容器的时候对象的派生部分会被删除:
vector<Widget> vw;
class SpecialWidget: // SpecialWidget从上面的Widget派生
public Widget {...};
SpecialWidget sw;
vw.push_back(sw); // sw被当作基类对象拷入vw
// 当拷贝时它的特殊部分丢失了
分割问题暗示了把一个派生类对象插入基类对象的容器几乎总是错的。如果你希望结果对象表现为派生类对
象,比如,调用派生类的虚函数等,总是错的
一个使拷贝更高效、正确而且对分割问题免疫的简单的方式是建立指针的容器而不是对象的容器。也就是
说,不是建立一个Widget的容器,建立一个Widget*的容器。拷贝指针很快,它总是严密地做你希望的(指针
拷贝比特),而且当指针拷贝时没有分割。不幸的是,指针的容器有它们自己STL相关的头疼问题,防止内存泄露的问题。如果你想避免这些头疼并且仍要避开效率、正确性和分割问题,你可能会发现智能指针的容器是一个吸引人的选择。
STL进行了大量拷贝,但它通常设计为避
免不必要的对象拷贝,实际上,它也被实现为避免不必要的对象拷贝。和C和C++内建容器的行为做个对
比,下面的数组:
Widget w[maxNumWidgets]; // 建立一个大小为maxNumWidgets的Widgets数组
// 默认构造每个元素
即使我们一般只使用其中的一些或者我们立刻使用从某个地方获取(比如,一个文件)的值覆盖每个默认构
造的值,这也得构造maxNumWidgets个Widget对象。使用STL来代替数组,你可以使用一个可以在需要的时
候增长的vector:
vector<Widget> vw; // 建立一个0个Widget对象的vector
// 需要的时候可以扩展
我们也可以建立一个可以足够包含maxNumWidgets个Widget的空vector,但没有构造Widget:
vector<Widget> vw;
vw.reserve(maxNumWidgets);
和数组对比,STL容器更文明。它们只建立(通过拷贝)你需要的个数的对象,而且它们只在你指定的时候
做。是的,我们需要知道STL容器使用了拷贝,但是别忘了一个事实:比起数组它们仍然是一个进步。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白话机器学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值