Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉

STL中的容器相当“聪明”,它们提供了迭代器,以便进行向后和向前的遍历(通过begin、end、rbegin等);它们告诉你所包含的元素类型(通过它们的value_type类型定义);在插入和删除的过程中,它们自己进行必要的内存管理;它们报告自己有多少对象,最多能容纳多少对象(分别通过size和max_size);当然,当它们自身被析构时,它们自动析构所包含的每个对象。

有了这么“聪明”的容器,许多程序员不再考虑自己做善后清理工作。更糟糕的是,他们认为,容器会考虑为他们做这些事情。很多情况下,他们是对的。但当容器包含的是new的方式而分配的指针时,他们这么想就不正确了。没错,指针容器在自己被析构时会析构所包含的每个元素,但指针的“析构函数”不做任何事情!它当然也不会调用delete。

       结果,下面的代码直接导致资源泄露:

       void doSomething()
       {
             vector<Widget*>vwp;
              for (int I = 0; I < SOME_MAGIC_NUMBER; ++i)
              {
                     Vwp.push_back(new Widget);
                     …
              }                                                //在这里发生了Widget资源泄露
       }

当vwp的作用域结束时,它的元素全部被析构,但并没有改变通过new创建的对象没有被删除这一事实。删除这些对象是你的责任,不是vector的责任。这是vector的特性。只有你才知道这些指针是否应该被释放。

	void doSomething()
	{
		for (vector<Widget*>::iterator I = vwp.begin(); i != vwp.end(); ++i)
			delete *i;
	}

这样做能行,但只是在你对“能行“不那么挑剔时。一个问题是,新的for循环做的事情和for_each相同,但不如使用for_each看起来那么清楚。问题是,这段代码不是异常安全的。如果在想vwp中填充指针和从中删除指针的两个过程中间有异常抛出的话,同样会有资源泄露。

其一解决方法如下:

       struct DeleteObject
       {
              template<typename T>
              void operator() (const T* ptr) const
              {
                     delete ptr;
              }
       };
       void doSomething()
       {
              deque<SpecialString*> dssp;
              for_each (dssp.begin(), dssp.end(), DeleteObject());
       }

但它仍然不是异常安全的。如果在SpecialString已经被创建而对for_each的调用还没开始时就有异常被抛出,则会有资源泄露发生。

其二解决方法:

利用Boost库中的shared_ptr。

       void doSomething()
       {
              typedef boost::shared_ptr<Widget> SPW;   //SPW= “指向Widget的shared_ptr”
              vector<SPW> vwp;
              for (int I = 0; I < SOME_MAGIC_NUMBER; ++i)
                     vwp.push_back(SPW(new Widget));
                     …
       }      //这里不会有Widget泄露,即使在上面的代码中有异常被抛出

永远不要错误的认为:你可以通过创建auto_ptr的容器使指针被自动删除。这个想法很危险,具体解释请看Effective STL 第8条。

你要记住的是:STL容器很智能,但没有智能到知道是否该删除自己所包含的指针的程度。当你使用指针的容器,而其中的指针应该被删除时,为了避免资源泄露,你必须或者用引用计数形式的智能指针对象(比如Boost的shared_ptr)代替指针,或者容器被析构时手工删除其中的每个指针。

时间: 2024-12-29 11:33:35

Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉的相关文章

如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉

如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉 在STL中容器是智能的,可以在容器销毁时自动调用容器里对象的析构函数来销毁容器存储的对象. STL的容器虽然比较智能,但是没有智能到可以自动销毁new出来的指针对象. 所以在使用STL中的容器时,如果保存的是mew出来的对象的指针.如果在容器销毁之前没有把new出来的对象释放,会造成内存泄露. 解决方法版本一: 这种手工释放容器里new的对象不太可靠,菲异常安全的.如果在delete的时候爆出异常,那么还是会引起

Effective STL 第1条:慎重选择容器类型

C++提供了几种不同的容器供我们选择,这里简单回顾一下: 1.标准STL序列容器:vector.string.deque和list. 2.标准STL关联容器:set.multiset.map.和multimap. 3.非标准序列容器slist和rope.slist是一个单向链表,rope本质上是一"重型"string. 4.非标准关联容器hash_set.hash_multiset.hash_map和hash_multimap. 5.vector<char>作为string

js数组容器中包含的属性和方法概述

1..length----获取数组长度: var arr = [1,2,3,4,5]; console.log(arr.length) //5 2.shift()----删除原数组第一项,并返回删除元素的值:如果数组为空则返回undefined,直接在元数组上修改: var arr= [1,2,3,4,5]; var result = arr.shift(); console.log(arr) //[2,3,4,5] console.log(result) //1 3.unshift()----

Effective STL -- 容器

01.慎重选择容器类型 选择容器原则:需要考虑元素的排序情况,是否与标准相符,迭代器能力,元素布局,与C的兼容性,查找速度,引用计数,插入删除对事物语义的支持,某些操作是否会使迭代器无效,内存分配策略. vector 需要使用随机迭代器 容器中布局需要与C兼容 deque 需要使用随机迭代器 当大多数插入在头部和尾部时 在尾部插入不会是迭代器,指针,引用变为无效 string 需要使用随机迭代器 介意使用引用计数计数,避免使用string,rope.string不可行 介意在容器上使用swap导

Docker容器中的数据管理--RHEL7.3

容器中数据管理主要分为两种方式:数据卷 和 数据卷容器 数据卷 数据卷是一个可供容器使用的特殊目录,它绕过文件系统,类似于Linux下对目录执行mount操作 创建一个数据卷 在容器内创建数据卷挂载到/volume目录下,相当于容器间的共享目录: docker run -dti -v /volume1 --name test1 centos:ifconfig /bin/bash 将宿主机目录挂载到容器作为数据卷: docker run -dti -v /test:volume2:ro --nam

隔离 docker 容器中的用户

笔者在前文<理解 docker 容器中的 uid 和 gid>介绍了 docker 容器中的用户与宿主机上用户的关系,得出的结论是:docker 默认没有隔离宿主机用户和容器中的用户.如果你已经了解了 Linux 的 user namespace 技术(参考<Linux Namespace : User>),那么自然会问:docker 为什么不利用 Linux user namespace 实现用户的隔离呢?事实上,docker 已经实现了相关的功能,只是默认没有启用而已.笔者将在

spring在IoC容器中装配Bean详解

1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean实例放入bean缓存池: 应用程序使用bean. 1.2.基于xml的配置 (1)xml文件概述 xmlns------默认命名空间 xmlns:xsi-------标准命名空间,用于指定自定义命名空间的schema文件 xmlns:xxx="aaaaa"-------自定义命名空间,xx

在 docker 容器中捕获信号

原文:在 docker 容器中捕获信号 我们可能都使用过 docker stop 命令来停止正在运行的容器,有时可能会使用 docker kill 命令强行关闭容器或者把某个信号传递给容器中的进程.这些操作的本质都是通过从主机向容器发送信号实现主机与容器中程序的交互.比如我们可以向容器中的应用发送一个重新加载信号,容器中的应用程序在接到信号后执行相应的处理程序完成重新加载配置文件的任务.本文将介绍在 docker 容器中捕获信号的基本知识. 信号(linux) 信号是一种进程间通信的形式.一个信

Docker拷贝宿主机与容器中的文件

如果我们需要将宿主机文件拷贝到容器内可以使用 docker cp 命令,也可以将文件从容器内拷贝到宿主机 将宿主机文件拷贝到容器内 docker cp 要拷贝的宿主机文件或目录 容器名称:容器文件或目录 1. 在宿主机上创建一个mengxuegu文件并写入内容 2. 将mengxuegu文件拷贝进 mycentos2 容器中的 /opt 目录下(mycentos2要是UP启动状态) 3. 登录 mycentos2 容器,查看/opt目录下是否有 mengxuegu 文件 从容器内文件拷贝到宿主机