一.问题的引出:

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

二.问题的分析过程:

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 Fragment 嵌套使用报错

    在新的SDK每次创建activity时,会自己主动生成  <pre name="code" class="java">public static c ...

  2. iOS开发宝典:String用法大全

    一.NSString 创建字符串.  NSString *astring = @"This is a String!"; 创建空字符串,给予赋值.  NSString *astri ...

  3. Java关键字static

    链接地址:http://www.cnblogs.com/devinzhang/archive/2011/12/13/2286367.html static表示“全局”或者“静态”的意思,用来修饰成员变 ...

  4. jvm 调优 工具

    1.用于打开运行中的jvm进程的gc 监控日志以及查看相关参数设置:jinfo 2.其它工具如:jps.jstack.jstat.jmap

  5. python3.4 使用pymssql 乱码

    问题:sqlserver数据库编码为gbk,使用python3.4+pymssql 查询,中文乱码 解决办法: conn = pymssql.connect(host="192.168.12 ...

  6. Word2007中如何插入参考文献

    很多国内的期刊杂志都只能使用word模板,导致插入参考文献成了件麻烦事,这时特别怀念Latex的便捷.于是找到一篇介绍word2007里插入参考文献的好方法,就是利用尾注的方法使文章的参考文献标号可以 ...

  7. 帝国cms后台 不同栏目发布字段不同

    在同一数据表下的两个栏目,由于功能不同,也需要建立不同的字段,问题是建立完不同字段后,其数据表下的两个栏目都有发布字段.这里教大家,不同栏目下发布内容,不同字段.修改数据模型中 录入表单模板 代码.底 ...

  8. cpu卡,sam卡原理

    第一部分 CPU基础知识一.为什么用CPU卡IC卡从接口方式上分,可以分为接触式IC卡.非接触式IC卡及复合卡.从器件技术上分,可分为非加密存储卡.加密存储卡及CPU卡.非加密卡没有安全性,可以任意改 ...

  9. BZOJ 1005 明明的烦恼 (组合数学)

    题解:n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数. 则               所以要求在n-2大小的数组中插入tot各序号,共有种插法: 在tot各序号排列中,插第一个节点的 ...

  10. PHP第一章学习——了解PHP(下)

    继续昨天的部分! —————————————————————————————— 首先Ubuntu下安装Apache软件: ubuntu更新源有问题,又要解决半天! 我现在很冷静! 安装Apache教程 ...