一.问题的引出:

容器可以存放对象,可以存放指针,这里要谈的是两者的使用问题。就是什么时候存放对象更好,什么时候存放指针更好?

二.问题的分析过程:

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容器里存放对象还是指针的更多相关文章

  1. c++继承构造子类调用父类构造函数的问题及关于容器指针的问题及当容器里储存指针时,记得要手动释放

    看下面的一个问题: class Person { private: string name; public: Person(const string& s=""){ nam ...

  2. stl 存放对象析构问题

    vector内数据使用结构体的话是深拷贝,vector内的数据会拷贝一份保存,vector内数据不会丢失.如果vector内数据是指针的话是进行浅拷贝,数据超出作用域后会自动析构,vector内所指向 ...

  3. c++ STL 常用容器元素类型相关限制 指针 引用

    c++ 的 STL 中主要有 vector , list, map, set  , multimap,multiset 这些容器完全支持使用内置类型和指针(指针注意内存泄露问题). 就是说乱用智能指针 ...

  4. 刷题常用的STL容器总结

    本文归纳总结刷题常用到STL容器以及一些标准算法,主要包括: string.vector.map.pair.unordered_map.set.queue.priority_queue.stack,以 ...

  5. 【转】c++中Vector等STL容器的自定义排序

    如果要自己定义STL容器的元素类最好满足STL容器对元素的要求    必须要求:     1.Copy构造函数     2.赋值=操作符     3.能够销毁对象的析构函数    另外:     1. ...

  6. STL容器的适用情况

     转自http://hsw625728.blog.163.com/blog/static/3957072820091116114655254/ ly; mso-default-props:yes; m ...

  7. DLL中传递STL参数,vector对象作为dll参数传递等问题(转)

    STL跨平台调用会出现很多异常,你可以试试. STL使用模板生成,当我们使用模板的时候,每一个EXE,和DLL都在编译器产生了自己的代码,导致模板所使用的静态成员不同步,所以出现数据传递的各种问题,下 ...

  8. STL容器存储的内容动态分配情况下的内存管理

    主要分两种情况:存储的内容是指针:存储的内容是实际对象. 看以下两段代码, typedef pair<VirObjTYPE, std::list<CheckID>*> VirO ...

  9. STL容器 erase的使用陷井

    http://www.cppblog.com/beautykingdom/archive/2008/07/09/55760.aspx?opt=admin 在STL(标准模板库)中经常会碰到要删除容器中 ...

随机推荐

  1. android媒体--图库与API层MediaPlayer的交互

    众所周知一个媒体播放器新建的几个步骤: Mediaplayer mp = new MediaPlayer(0 mp.setDatasource(xxx); mp.setDispalyer(xxx); ...

  2. HDU3415:Max Sum of Max-K-sub-sequence(单调队列)

    Problem Description Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence means the left ...

  3. zoj 2229 Ride to School

    所有车子到达的总时间算出来,然后从小到大排序,如果:1. 开始时间 < 0 的,不予考虑,太快的赶不上,太慢的赶上也没用.2. 开始时间 > 0 的,Charley 和最早到达的车子一起到 ...

  4. C#复习一( Twenty Days)

      今天开始将要复习最近所学的一些C#知识.下面就来总结一下今天所复习的内容,以此来巩固我对C#知识的掌握.今天主要以举几个程序为例. 首先还是要注意代码的规范: –注释//,/**/,/// –骆驼 ...

  5. Tomcat 启动 Debug模式

    如果debug启动遇到如下错误: ERROR: transport error 202: gethostbyname: unknown host ERROR: JDWP Transport dt_so ...

  6. UVA 10798 - Be wary of Roses (bfs+hash)

    10798 - Be wary of Roses You've always been proud of your prize rose garden. However, some jealous f ...

  7. Java format 简单应用

    一.前言 String.format 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能,为了不止步于简单调用 String.format("Hello %s", " ...

  8. python自学笔记(十一)关于函数及书写格式

    1.函数是抽象的第一步       1.1 有关高压锅     1.2 函数是抽象出来的结构,是总结,是方法     1.3 多用函数     2.如何定义函数        2.1 def是关键词, ...

  9. 基于FPGA的cordic算法的verilog初步实现

    最近在看cordic算法,由于还不会使用matlab,真是痛苦,一系列的笔算才大概明白了这个算法是怎么回事.于是尝试用verilog来实现.用verilog实现之前先参考软件的程序,于是先看了此博文h ...

  10. 解决用户 'IIS APPPOOL\Classic .NET AppPool' 登录失败

    解决用户 'IIS APPPOOL\Classic .NET AppPool' 登录失败 windows 7进入iis管理器 本地应用程序池 选中classic. net appPool 选择右侧的 ...