转:STL容器里存放对象还是指针
一.问题的引出:
容器可以存放对象,可以存放指针,这里要谈的是两者的使用问题。就是什么时候存放对象更好,什么时候存放指针更好?
二.问题的分析过程:
1. 首先说下stl容器的工作方式
对于内建类型(int float char等),容器的工作方式是纯粹的位拷贝,这里没有什么需要多说的。
对于自定义的对象,容器容纳了对象(比如通过insert或push_back等),但容器中存放的对象不是你给它们的那个对象,因为两个对象在内存中的位置不一样。此外,当你从容器中获取一个对象时,你所得到的对象不是容器里的那个对象。取而代之的是,当你向容器中添加一个对象(比如通过insert或push_back等),进入容器的是你指定的对象的拷贝。拷进去,拷出来。拷贝是STL的方式。
下面的例子可以说明这点:
int main()
{
vector
//我们的对象
Object data;
data.setName("Data");
//对象插入到vector后面
v.push_back(data);
cout << "Address of data : &data = " << &data << " ";
cout << "Address of data in vector: &v[0] = " << &v[0] << " ";
system("pause") ;
return 0;
}
输出如下:
2. 存放对象的情况
明白了容器的工作方式,那么进一步来讨论容器存放对象和指针在操作过程中的开销。内建类型的数据进行拷贝的方式是位拷贝,自定义类型的数据进行拷贝会调用类的拷贝构造函数,这个函数是每个类都有的,如果类中没有显式的声明那么编译器也会给提供一个默认的拷贝构造函数。如果一个类的数据非常多,或者包含其他复杂自定义类型,那么此时类的拷贝构造的开销是非常大的。
此时容器中要是存放的是对象vector,那么一个简单的插入操作的代价也是惊人的,更别说什么排序之类的操作,很容易带来性能上的瓶颈,这个时候就需要在容器中存放对象的指针vector,这样的话就避免了这些多余的拷贝消耗,因为指针就是一个机器字长,拷贝的代价可以忽略不计。
写了个测试代码如下:
typedef std::vector ObjectVector;
typedef std::vector PointerVector;
//下面是存贮对象的情况
begin = GetTickCount();
ObjectVector objectVector;
for (DWORD i = 0; i < MAX; i++)
{
objectVector.push_back(*pCom);
}
end = GetTickCount();
cout << "存放对象消耗的时间:";
cout << end - begin << "毫秒 ";
//下面是存贮指针的情况
begin = GetTickCount();
PointerVector pinterVector;
for (DWORD i = 0; i < MAX; i++)
{
pinterVector.push_back(pCom);
}
end = GetTickCount();
cout << "存放指针消耗的时间:";
cout << end - begin << "毫秒 ";
下面的结果是在Release版本下,并且编译器的优化关闭的情况下,这和我们目前的客户端设置一样:
MAX = 4000
上面的数据没有用统计学的方法去测试,只是取了一次结果,我测试了多次结果在数量级上是一样的(用上面的数据只是说明拷贝的代价是巨大的,并没有强调必须用指针)。
分析完了拷贝的性能消耗,再看看另一个问题,就是声明了一个存放基类对象的容器,如果此时向容器中插入子类的对象,那么子类特有的那些内容就会被无情剥离(slicing)。这是一个很严重的问题。解决的方法还是使用基于指针的容器。
2. 存放指针的情况
上面提到的两个问题用指针确实比用对象好,问题不是这么绝对。在上面考虑拷贝消耗的时候有个前提:如果一个类的数据非常多,或者包含其他复杂自定义类型,并且需要大量的使用需要容器内部对象拷贝的操作。如果一个对象中就是几个简单的内建类型,或者干脆就是一个简单的内建类型的数据,那么再用指针可真是得不偿失了,因为使用指针需要程序员去管理内存。完全没有必要为了节省几个int类型的拷贝消耗而去自己去做内存的管理,确实完全没有必要。
用指针就需要自己手动的去管理这些指针所指向的内存,stl容器确实可以动态申请内存使自己变大以容纳更多的元素,但这些动态空间存放的是你的指针,而并不是你指针指向的动态内存,你的指针内存当然需要你去管理,如果实在不想做这些管理工作,可以去使用智能指针。(存储指针,还需要我们 delete)
三.问题的总结:
通过上面的分析,总结了一下几点:
1. Stl容器可以存放内建类型、自定义类型、指针类型的元素。
2. 元素如果是内置数据类型,那么就存放数据本身。
3. 元素如果是复杂类型,并且在使用容器的过程中需要容器的元素进行大量的拷贝操作的时候,就要考虑在容器中放入指针;
4. 存放指针容易出现内存的泄露,所以在使用的时候需要考虑清楚,如能接口设计的合理,能保证容器在使用的过程中不进行大量的拷贝工作,在容器中存放对象是最好的了。
5. 使用智能指针是一种两种优点都兼备的,既有指针的操作效率,又避免了自己手动管理内存带来的问题。
6. 指针可以解决派生类对象存放在使用基类实例化的容器中的剥离(slicing)问题。
在考虑容器中是存放对象还是指针的时候脑子里时刻要想到,我的操作需要容器做多少拷贝工作,这些拷贝操作带来的损耗能否接受,从这个本质问题上把握好了,选择起来就不是问题了,要根据实际情况灵活运用。
转自:http://hsw625728.blog.163.com/blog/static/3957072820091116115732821/
转:STL容器里存放对象还是指针的更多相关文章
- c++继承构造子类调用父类构造函数的问题及关于容器指针的问题及当容器里储存指针时,记得要手动释放
看下面的一个问题: class Person { private: string name; public: Person(const string& s=""){ nam ...
- stl 存放对象析构问题
vector内数据使用结构体的话是深拷贝,vector内的数据会拷贝一份保存,vector内数据不会丢失.如果vector内数据是指针的话是进行浅拷贝,数据超出作用域后会自动析构,vector内所指向 ...
- c++ STL 常用容器元素类型相关限制 指针 引用
c++ 的 STL 中主要有 vector , list, map, set , multimap,multiset 这些容器完全支持使用内置类型和指针(指针注意内存泄露问题). 就是说乱用智能指针 ...
- 刷题常用的STL容器总结
本文归纳总结刷题常用到STL容器以及一些标准算法,主要包括: string.vector.map.pair.unordered_map.set.queue.priority_queue.stack,以 ...
- 【转】c++中Vector等STL容器的自定义排序
如果要自己定义STL容器的元素类最好满足STL容器对元素的要求 必须要求: 1.Copy构造函数 2.赋值=操作符 3.能够销毁对象的析构函数 另外: 1. ...
- STL容器的适用情况
转自http://hsw625728.blog.163.com/blog/static/3957072820091116114655254/ ly; mso-default-props:yes; m ...
- DLL中传递STL参数,vector对象作为dll参数传递等问题(转)
STL跨平台调用会出现很多异常,你可以试试. STL使用模板生成,当我们使用模板的时候,每一个EXE,和DLL都在编译器产生了自己的代码,导致模板所使用的静态成员不同步,所以出现数据传递的各种问题,下 ...
- STL容器存储的内容动态分配情况下的内存管理
主要分两种情况:存储的内容是指针:存储的内容是实际对象. 看以下两段代码, typedef pair<VirObjTYPE, std::list<CheckID>*> VirO ...
- STL容器 erase的使用陷井
http://www.cppblog.com/beautykingdom/archive/2008/07/09/55760.aspx?opt=admin 在STL(标准模板库)中经常会碰到要删除容器中 ...
随机推荐
- 第七届河南省赛A.物资调度(dfs)
10401: A.物资调度 Time Limit: 2 Sec Memory Limit: 128 MB Submit: 95 Solved: 54 [Submit][Status][Web Bo ...
- [ACM] POJ 2253 Frogger (最短路径变形,每条通路中的最长边的最小值)
Frogger Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 24879 Accepted: 8076 Descript ...
- iOS中Block介绍(一)基础
ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...
- POJ 2142:The Balance_扩展欧几里得(多组解)
先做出两个函数的图像,然后求|x|+|y|的最小值.|x|+|y|=|x0+b/d *t |+|y0-a/d *t| 这个关于t的函数的最小值应该在t零点附近(在斜率大的那条折线的零点附近,可以观察出 ...
- Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
链接地址:http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html 使用TCP/IP的套接字(Socket)进行通信 套接字Socke ...
- Java学习之equals和==的区别
转自:http://www.cnblogs.com/zhxhdean/archive/2011/03/25/1995431.html java中的数据类型,可分为两类: 1.基本数据类型 也称原始数 ...
- Spring--依赖注入
Spring 能有效地组织J2EE应用各层的对象.不管是控 制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.Spring将 ...
- 10条PHP编程习惯
过去的几周对我来说是一段相当复杂的经历.我们公司进行了大裁员,我是其中之一,但却体验到了其中的乐 趣.我从来没有被开除过,所以很难不去想得太多.我开始浏览招聘板块,一个全职PHP程序员的职位很吸引人, ...
- 射频识别技术漫谈(7)——ID卡
ID(Identification)是识别的意思,ID卡就是识别卡.ID卡包含范围广泛,只要具有识别功能的卡片都可以叫ID卡,例如条码卡,磁卡都可以是ID卡,我们这儿说的当然是射频识别卡. 射频ID卡 ...
- PC游戏编程(入门篇)(前言写的很不错)
PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...