为什么不能将一个const类对象对象放入STL容器

相关文章推荐
int main()
//没有安全性检查#include iostream>#include fstream>struct A{};in...
虽是细节,但使用不当,也容易造成严重错误。挖过填过的坑,记录一下。
主要有以下几种方法
list.push_back(1);
list.push_back(2);
一、初始...
小记一下:
一、初始化构造
vector v1(v2);
二、swap赋值
vector v1();
v1.swap(v2);
// v1 = v2
三、assig...
前面十二个算法所展现的都属于非变易算法(Non-mutating algorithms)系列,现在我们来看看变易算法。所谓变易算法(Mutating algorithms)就是一组能够修改容器元素数据...
vector中存放自定义类的前提是:
自定义的类必须有默认构造函数。因为vector会调用默认构造函数来初始化元素的对象。
那必须要明确:编译器什么时候隐式声明默认构造函数?
有两个条件:
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
关于 vector 中不能存放引用,这是一个在初始C++时候就应该知道的问题,但是我居然没注意,还好及时发现了。
《C++ primer》上说 vector 中不能存放引用的原因是:引用不支...
《程序员面试宝典》中stl模板与容器中的一个例子:
结论:vector内数据使用结构体的话是深拷贝,vector内的数据会拷贝一份保存,vector内数据不会丢失。如果vector内数据是指针的话是进行浅拷贝,数据超出作用域后会自动析构,vector内...
他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)为什么auto&ptr智能指针不能作为STL标准容器的元素
上个星期的博客shared_ptr源码剖析里其实遗漏了一个问题:为什么auto_ptr不可以作为STL标准容器的元素,而shared_ptr可以?
我在网上看了好多篇讲shared_ptr的文章里讲到了这个问题,不过大多文章只是简单两笔带过。我研究了一下这个问题,发现还是有挺多有价值的内容,所以把这个问题单独成一篇博客和大家分享。
先从表象上看看这个问题,假如有这样的一段代码,是否能够运行?
int costa_foo()
vector& auto_ptr&int& & v(10);
for(;i&10;i++)
v[i]=auto_ptr&int&(new int(i));
答案是否定的,甚至这段代码是无法编译通过的。g++编译器会报下面这个错误:
In file included from /usr/include/c++/4.4/memory:51,
from foo.cpp:x:
/usr/include/c++/4.4/bits/stl_construct.h: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = std::auto_ptr&int&, _T2 = std::auto_ptr&int&]’:
/usr/include/c++/4.4/bits/stl_uninitialized.h:187:
instantiated from ‘static void std::__uninitialized_fill_n&&anonymous& &::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = std::auto_ptr&int&*, _Size = unsigned int, _Tp = std::auto_ptr&int&, bool &anonymous& = false]’
/usr/include/c++/4.4/bits/stl_uninitialized.h:223:
instantiated from ‘void std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = std::auto_ptr&int&*, _Size = unsigned int, _Tp = std::auto_ptr&int&]’
/usr/include/c++/4.4/bits/stl_uninitialized.h:318:
instantiated from ‘void std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator&_Tp2&&) [with _ForwardIterator = std::auto_ptr&int&*, _Size = unsigned int, _Tp = std::auto_ptr&int&, _Tp2 = std::auto_ptr&int&]’
/usr/include/c++/4.4/bits/stl_vector.h:1035:
instantiated from ‘void std::vector&_Tp, _Alloc&::_M_fill_initialize(size_t, const _Tp&) [with _Tp = std::auto_ptr&int&, _Alloc = std::allocator&std::auto_ptr&int& &]’
/usr/include/c++/4.4/bits/stl_vector.h:230:
instantiated from ‘std::vector&_Tp, _Alloc&::vector(size_t, const _Tp&, const _Alloc&) [with _Tp = std::auto_ptr&int&, _Alloc = std::allocator&std::auto_ptr&int& &]’
foo.cpp:22:
instantiated from here
/usr/include/c++/4.4/bits/stl_construct.h:74: error: passing ‘const std::auto_ptr&int&’ as ‘this’ argument of ‘std::auto_ptr&_Tp&::operator std::auto_ptr_ref&_Tp1&() [with _Tp1 = int, _Tp = int]’ discards qualifiers
错误出在这一行:
vector& auto_ptr&int& & v(10);
这个错误是什么含义呢,我们看stl_construct.h的74行所在的函数:
* Constructs an object in existing memory by invoking an allocated
* object's constructor with an initializer.
template&typename _T1, typename _T2&
inline void
_Construct(_T1* __p, const _T2& __value)
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_]allocator::construct
::new(static_cast&void*&(__p)) _T1(__value);
我来直接说这个函数的作用:把第二个参数__T2& value拷贝构造一份,然后复制到T1这个指针所指向的位置。它是如何做到的呢?
看第第74行, 这里使用new的方法和我们平常所见到的似乎略有不同。 这是一个placement new。 placement new的语法是:
new(p) T(value)
placement new并不会去堆上申请一块内存,而是直接使用指针p指向的内存,将value对象拷贝一份放到p指向的内存上去。
看到这里我就知道为什么编译器在编译本文开头的那段代码时会报这段错误” /usr/include/c++/4.4/bits/stl_construct.h:74: error: passing ‘const std::auto_ptr&int&’ as ‘this’ argument of ‘std::auto_ptr&_Tp&::operator std::auto_ptr_ref&_Tp1&() [with _Tp1 = int, _Tp = int]’ discards qualifiers" 了。通过查看auto_ptr的源代码(位置在 c++头文件目录的 backward/auto_ptr.h)可以发现, auto_ptr并没有一个参数为const auto_ptr& 的拷贝构造函数。换而言之, auto_ptr进行拷贝构造的时候,必需要修改作为参数传进来的那个auto_ptr。
auto_ptr的拷贝构造函数是这样写的:
auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }
可以看出来, 在拷贝构造一个auto_tr的时候, 必需要把参数的那个auto_ptr给release掉,然后在把参数的_M_ptr指针赋值给自己的_M_ptr指针。补充说明一下, _M_ptr是auto_ptr的一个成员,指向auto_ptr管理的那块内存,也就是在auto_ptr生命周期之外被释放掉的那块内存。
当看到这里的时候,整个问题已经能解释通了。STL容器在分配内存的时候,必须要能够拷贝构造容器的元素。而且拷贝构造的时候,不能修改原来元素的值。而auto_ptr在拷贝构造的时候,一定会修改元素的值。所以STL元素不能使用auto_ptr。
不过,还有一个很重要的问题没有解释。那就是为什么在设计auto_ptr的时候,拷贝构造要修改参数的值呢?
其实这问题很简单,不看代码也可以解释清楚。auto_ptr内部有一个指针成员_M_ptr,指向它所管理的那块内存。而拷贝构造的时候,首先把参数的_M_ptr的值赋值给自己的_M_ptr,然后把参数的_M_ptr指针设成NULL,。如果不这样设计,而是直接把参数的_M_ptr指针赋值给自己的, 那么两个auto_ptr的_M_ptr指向同一块内存,在析构auto_ptr的时候就会出问题: 假如两个auto_ptr的_M_ptr指针指向了同一块内存,那么第一个析构的那个auto_ptr指针就把_M_ptr指向的内存释放掉了,造成后一个auto_ptr在析构时释要放一块已经被释放掉的内存,这明显不科学,会产生程序的段错误而crash掉。
而shared_ptr则不存在这个问题, 在拷贝构造和赋值操作的时候,只会引起公用的引用计数的+1,不存在拷贝构造和赋值操作的参数不能是const的问题。
1 auto_ptr不能作为STL标准容器的元素。
2 auto_ptr在拷贝复制和赋值操作时,都会改变参数。这是因为两个auto_ptr不能管理同一块内存。
----------------------------------------------------------------------------------
看来真是有学而时习之的必要。 10分钟之前我竟然在搜这个问题,而全然忘了以前自己研究过。
不过看到stackoverflow上的回答觉得比我写的简单多了, 抄过来:
The C++ Standard says that an STL element must be "copy-constructible" and "assignable." In other words, an element must be able to be assigned or copied and the two elements are logically independent.std::auto_ptrdoes not fulfill this requirement.
Take for example this code:
std::vector&std::auto_ptr&X& & vecX;
vecX.push_back(new X);
std::auto_ptr&X& pX = vecX[0];
// vecX[0] is assigned NULL
To overcome this limitation, you should use the ,
smart pointers or the boost equivalents if you don't have C++11.
然后又去翻了一下auto_ptr的源码,
看到operator =的代码这样写的:
operator=(auto_ptr& __a) throw()
reset(__a.release());
多个auto_ptr不能管理同一片内存, 执行=的时候,就把原来的auto_ptr给干掉。其实从逻辑上来讲,如果多个auto_ptr管理同一块内存肯定有问题。第一个auto_ptr析构的时候就应该把内存释放掉了, 第二个auto_ptr再析构的时候,就要去释放已经被释放的内存了, 程序肯定挂掉。
Copyright (C) , All Rights Reserved.
版权所有 闽ICP备号
processed in 0.045 (s). 12 q(s)在STL(标准模板库)中经常会碰到要删除容器中部分元素的情况,本人在编程中就经常编写这方面的代码,在编码和测试过程中发现在STL中删除容器有很多陷阱,网上也有不少网友提到如何在STL中安全删除元素这些问题。本文将讨论编程过程中最经常使用的两个序列式容器vector、list中安全删除元素的方法和应该注意的问题,&&&&&& 其它如queue、stack等配接器容器(container adapter),由于它们有专属的操作行为,没有迭代器(iterator),不能采用本文介绍的删除方法,至于deque,它与vector的删除方法一样。STL容器功能强大,but no siliver bullet,如果你使用不当,也将让你吃尽苦头。&&& 这里最重要的是要理解erase成员函数,它删除了itVect迭代器指向的元素,并且返回要被删除的itVect之后的迭代器,迭代器相当于一个智能指 针,指向容器中的元素,现在删除了这个元素,将导致内存重新分配,相应指向这个元素的迭代器之后的迭代器就失效了,但erase成员函数返回要被删除的 itVect之后的迭代器。&
1.手工编写for循环代码删除STL序列式容器中元素的方法例如,你能看出以下代码有什么问题?例1:#include &iostream&#include &vector&void main( ) {&&&&&& vector&int& vectI&&&&&&&&&&&& //&&&& 初始化vector容器&&&&&& for (i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&& }&&&&&& //&&&& 以下代码是要删除所有值为4的元素&&&&&& vector&int&::iterator itVect = vectInt.begin();&&&&&& for ( ; itVect != vectInt.end();&& ++itVect ) {&&&&&&&&&&&&&& if ( *itVect == 4 ) {&&&&&&&&&&&&&&&&&&&& vectInt.erase( itVect );&&&&&&&&&&&&&& }&&&&&& }&&&&&& int iSize = vectInt.size();&&&&&& for (&& i = 0 ; i & iS i++ )&& {&&&&&&&&&&&&&&&&&&&& cout && " i= " && i &&&& ", " && vectInt[ i ] &&&&&&&& }}
&&& 例1将导致程序未定义的错误,在windows中即是访问非法内存,程序当掉。因为vectInt.erase( itVect );调用后itVect之后的迭代器已无效了,所以当执行++itVect后,*itVect访问了非法内存。例1也是初学者最容易犯的错误,这个错误也 比较容易发现。例2:#include &iostream&#include &vector&void main( ) {&&&&&& vector&int& vectI&&&&&&&&&&&& //&&&& 初始化vector容器&&&&&& for ( i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& if ( 3 == i ) {&&&&&&&&&&&&&&&&&&&& //&&&&&& 使3的元素有两个,并且相临。这非常关键,否则将发现不了bug。&&&&&&&&&&&&&&&&&&&& //&& 具体解释见下。&&&&&&&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& }&&&&&& }&&&&&& vector&int&::iterator itVect = vectInt.begin();&&&&&& vector&int&::iterator itVectEnd = vectInt.end(); //&&&&&& 防止for多重计算&&&&&& //&&&& 以下代码是要删除所有值为3的元素&&&&&& for ( ; itVect != itVectE ++itVect ) {&&&&&&&&&&&&&& if ( *itVect == 3 ) {&&&&&&&&&&&&&&&&&&&& itVect = vectInt.erase( itVect );&&&&&&&&&&&&&& }&&&&&& }&&&&&& int iSize = vectInt.size();&&&&&& for (&& i = 0 ; i & iS i++ )&& {&&&&&&&&&&&&&&&&&&&& cout && " i= " && i &&&& ", " && vectInt[ i ] &&&&&&&& }
例2可能会导致不能把vectInt中所有为3的元素删除掉。因为第一次删除成功时,itVect = vectInt.erase( itVect );itVect为指向3之后的位置,之后再执行++itVect,itVect就掉过了被删除元素3之后的元素3,导致只删除了一个为3的元素,这个 bug比较隐蔽,因为如果不是两个均为3的元素相临,就将很难捕捉到这个bug,程序有可能在一段时间运行良好,但如碰到容器中两值相同的元素相临,则程 序就要出问题。&
例3:#include &iostream&#include &vector&void main( ) {&&&&&& vector&int& vectInt( 5 );&&&&&&&&&&&& vectInt[ 0 ] = 0;&&&&&& vectInt[ 1 ] = 1;&&&&&& vectInt[ 2 ] = 2;&&&&&& vectInt[ 3 ] = 3;&&&&&& vectInt[ 4 ] = 4; //&&&& 替换为 vectInt[ 4 ] = 3;试试&&&&&& vector&int&::iterator itVect = vectInt.begin();&&&&&& vector&int&::iterator itVectEnd = vectInt.end(); //&&&&&& 防止for多重计算&&&&&& //&&&& 以下代码是要删除所有值为3的元素&&&&&& for ( ; itVect != itVectE ) {&&&&&&&&&&&&&& if ( *itVect == 3 ) {&&&&&&&&&&&&&&&&&&&& itVect = vectInt.erase( itVect );&&&&&&&&&&&&&& }&&&&&&&&&&&&&& else {&&&&&&&&&&&&&&&&&&&& ++itV&&&&&&&&&&&&&& }&&&&&& }&&&&&& int iSize = vectInt.size();&&&&&& for (&& i = 0 ; i & iS i++ )&& {&&&&&&&&&&&&&&&&&&&& cout && " i= " && i &&&& ", " && vectInt[ i ] &&&&&&&& }}例3,对于本例你可能要说程序没有任何问题,解决了上面的两个bug,程序也运行正常。但且慢,你把 &vectInt[ 4 ] = 4;& 这一行改为 &vectInt[ 4 ] = 3;&试试,一运行,程序当掉,访问非法内存!你疑惑不解:从程序看不出bug,而且我还把vectInt.end()放在外面计算以防止for多重计算,提高效率。哈哈,问题就出在最后一句话!算法大师Donald Knuth有一句名言:不成熟的优化是一切恶果的根源( Permature optimization is the root of all evil )。由于在for循环中要删除元素,则vectInt.end()是会变化的,所以不能在for循环外计算,而是每删除一次都要重新计算,所以应放在 for循环内。那你要问,为什么把 &vectInt[ 4 ] = 4;& 这一行改为 &vectInt[ 4 ] = 3;&程序就会当掉,而不改程序就很正常呢?这就跟vector的实现机制有关了。下面以图例详细解释。vectInt的初始状态为:& & & & & &&&&&&&&&&&&&&&&&&&&&&& | end0&& 1&& 2&& 3&& 4&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&删除3后,&&&&&&&&& & & &&&&&&&& |新的end&& | 原来的end0&& 1&& 2 & 4&& 4&&&&&注意上面&新的end&指向的内存并没有被清除,为了效率,vector会申请超过需要的内存保存数据,删除数据时也不会把多余的内存删除。删除后多余内存并没有清0。然后itVect再执行++itVect,因为此时*itVect等于4,所以继续循环, 这时itVect 等于&新的end&但不等于&原来的end&(它即为itVectEnd),所以继续,因为 *itVect访问的是只读内存得到的值为4,不等于3,故不删除,然后执行++itVect此时itVect等于itVectEnd退出循环。从上面过程可以看出,程序多循环了一次(删除几次,就要多循环几次),但程序正常运行。如果把 &vectInt[ 4 ] = 4;& 这一行改为 &vectInt[ 4 ] = 3;&过程如下:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& | end0&&&&&&&& 1&&&&&&&& 2&&&&&&&& 3&&&&&&&& 3&&&&
&&删除3后,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |新的end&&&&&& |原来的 end0&&&&&&&& 1&&&&&&&& 2&&&&&&&& 3&&&&&&&& 3&&&&&&删除第2个3后,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |新的end&&&&&&&&&&&& |原来的 end0&&&&&&&& 1&&&&&&&& 2&&&&&&&& 3&&&&&&&& 3&&&&&&这时itVect 等于&新的end&但不等于&原来的end&(它即为itVectEnd),所以继续,因为 *itVect访问的是只读内存得到的值为3,等于3,所以执行删除,但因为*itVect访问的是只读内存不能删除,所以程序当掉。综上,我们知道当要删除的值在容器末尾时,会导致程序删除非法内存,程序当掉;即使程序正常运行,也是for循环多执行了等于删除个数的循环。所以把 vectInt.end()放在for循环外面执行,完全是错误的。对于list容器,list.end()在删除过程中是不会变的,可以把它放在for 循环外面计算,但由于list.end()是个常量,把list.end()放在for循环中计算编译器应该可以优化它。从安全考虑,除非你能保证for 循环中不会改变容器的大小,否则都应该对容器的值在for循环中计算,对于 vectInt.size()这样的计算,也应该在for循环中计算,不要因为微小的优化而导致程序出错。正确的方法:例4:#include &iostream&#include &vector&void main( ) {&&&&&& vector&int& vectI&&&&&&&&&&&& for (&& i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& if ( 3 == i ) {&&&&&&&&&&&&&&&&&&&& //&&&&&& 使3的元素有两个,并且相临。&&&&&&&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& }&&&&&& }&&&&&& vector&int&::iterator itVect = vectInt.begin();&&&&&& //&&&& 以下代码是要删除所有值为3的元素&&&&&& for ( ; itVect != vectInt.end();&& ) {&& // 删除 ++itVect{&&&&&&&&&&&&&& if ( *itVect == 3 ) {&&&&&&&&&&&&&&&&&&&& itVect = vectInt.erase( itVect );&&&&&&&&&&&&&& }&&&&&&&&&&&&&& else {&&&&&&&&&&&&&&&&&&&&& ++itV&&&&&&&&&&&&&& }&&&&&& }&&&&&& //&&&& 把vectInt.size()放在for循环中&&&&&& for (&& i = 0 ; i & vectInt.size(); i++ )&& {&&&&&&&&&&&&&&&&&&&& cout && " i= " && i &&&& ", " && vectInt[ i ] &&&&&&&& }运行结果为:i= 0, 0i= 1, 1i= 2, 2i= 3, 4从结果显示值为3的元素确实被删除了。2.使用STL中通用算法或容器成员函数删除元素的方法以上手工编写for循环代码删除容器中元素的方法也有一些问题,如果判断条件特别复杂,又有循环判断的话,循环中间又有异常处理的话,++itVect的位置就要小心放置了,稍不留意就要出错。所以手工编写代码删除容器中元素的方法不太安全,代码重复,也不够优雅,要注意的地方很多。对于这种情况,可以考虑使用STL中通用算法remvoe()和remove_if()帮忙。而remvoe()和remove_if()这两个算法也有一个问题需要程序员特别小心。在通用算法中的 remove(包括remove_if)函数,并不真正从容器中删除元素,而是&应被删除的元素&被其后的&未被删除的元素&覆盖。返回值ForwardIterator指向经移除后的最后元素的下一位置。如vector{0,1,2,3,3,4},执行remove(),希望移除所有值为3的元素,结果为{0,1,2,4,3,4},返回值 ForwardIterator指向第5个元素。即:0&&&&&&&& 1&&&&&&&& 2&&&&&&&& 3&&&&&&&& 3&&&&&& 4&& 移除前0&&&&&&&& 1&&&&&&&& 2&&&&&&&& 4&&&&&&&& 3&&&&&& 4&& 移除后移除值为3的元素。移除后3被其后的4替代,最后两位元素为残余数据。例 5:void main() {&&&&&& vector&int& vectI&&&&&&&&&&&& for (i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& if ( 3 == i ) {&&&&&&&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& }&&&&&& }&&&&&& remove( vectInt.begin(), vectInt.end(), 3 );&&&&&& cout && " after deleted , size = " && vectInt.size() &&&&&&&& for ( i = 0; i & vectInt.size();; i++ ) {&&&&&&&&&&&&&& cout && "i = " && i && " , " && vectInt[i] &&&&&&&& }}运行结果为:after deleted , size = 6 // 从这行可以看出,移除后容器的大小没变i = 0 , 0i = 1 , 1i = 2 , 2i = 3 , 4 //&& 从这行可以看出:&应被删除的元素&3 被其后的&未被删除的元素&4覆盖i = 4 , 3i = 5 , 4&&&&&所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数erase()做到。例 6:void main() {&&&&&& vector&int& vectI&&&&&&&&&&&& for (i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& if ( 3 == i ) {&&&&&&&&&&&&& &&&&&& vectInt.push_back( i );&&&&&&&&&&&&&& }&&&&&& }&&&&&& vectInt.erase( remove( vectInt.begin(), vectInt.end(), 3 ), vectInt.end() );&&&&&& cout && " after deleted , size = " && vectInt.size() &&&&&&&& for ( i = 0; i & vectInt.size();; i++ ) {&&&&&&&&&&&&&& cout && "i = " && i && " , " && vectInt[i] &&&&&&&& }}运行结果为:after deleted , size = 4 // 从这行可以看出,删除后容器的大小变化了i = 0 , 0i = 1 , 1i = 2 , 2i = 3 , 4从结果可以看出,所有值为3的元素确实被删除了。对于vector容器存放其他比较复杂的对象,就可以用remove_if()加函数对象(Function Object)的方法。如:例7:#include &iostream&#include &sstream&#include &string&#include &vector&#include &algorithm&#include &list&class CTest {public:&&&&&& CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }&&&&&& void vPrint() { cout && "name=" && m_strName && " price = " && m_iPrice &&&&&&&& }private:&&&&&& string m_strN&&&&&& int&& m_iP&&&&&& //&&&& 由于两个函数对象要访问CTest类的private成员,所以设为友员。&&&&&& friend class CStrF&&&&&& friend class CIntF};//&&&& 函数对象,根据string比较class CStrFunc {&&&&&& string m_public:&&&&&& CStrFunc( const string& str ) : m_str( str ) {&&&&&& }&&&&&& bool operator() ( const CTest& left ) {&&&&&&&&&&&&&& return ( m_str == left.m_strName ) ? true :&&&&&& }};//&&&& 函数对象,根据int比较class CIntFunc {&&&&&& int m_iPpublic:&&&&&& CIntFunc( int iPrice ) : m_iPrice( iPrice ) {&&&&&& }&&&&&& bool operator() ( const CTest& left ) {&&&&&&&&&&&&&& return ( m_iPrice == left.m_iPrice ) ? true :&&&&&& }};void main( ) {&&&&&& vector& CTest & vectT&&&&&&&&&&&& for (&& i = 0; i & 5 ; i++ ) {&&&&&&&&&&&&&& //&&&&&& 流格式化符,把int转化为string&&&&&&&&&&&&&& stream &&&&&&&&&&&&&&&& string str = stream.str();&&&&&&&&&&&&&& CTest clTest( str, i );&&&&&&&&&&&&&& vectTest.push_back( clTest );&&&&&& }&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ].vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_strName = "3"的元素&&&&&& vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ),&&&&&&&&&&&&&& vectTest.end() );&&&&&& cout && "delete 3 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ].vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_iPrice = 2的元素&&&&&& vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),&&&&&&&&&&&&&& vectTest.end() );&&&&&& cout && "delete 2 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ].vPrint();&&&&&& }}手工编写for循环代码删除STL序列式容器中元素的方法,使用STL中通用算法或容器成员函数删除元素的方法,两者之间的比较:1.&& 前者代码重复。2.&& 前者容易出错,不够清晰。3.&& 效率:0&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 2&&&&&& 3&&&&&&&&&&&&&&&& 2&&&&&&&&&& 5&&&&&& 6&&&&&&&&&&&&&&&& 70&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 3&&&&&& 2&&&&&&&&&&&&&&&& 5&&&&&&&&&& 6&&&&&& 70&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 3&&&&&& 5&&&&&&&&&&&&&&&& 6&&&&&&&&&& 7用第一种方法删除所有值为2的元素从上图可以看出,每删除一个元素,后面的所有元素都到往前移动一位,导致一次内存大搬迁。0&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 2&&&&&& 3&&&&&&&&&&&&&&&& 2&&&&&&&&&& 5&&&&&& 6&&&&&&&&&&&&&&&& 70&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 3&&&&&& 2&&&&&&&&&&&&&&&& 5&&&&&&&&&& 6&&&&&& 6&&&&&&&&&&&&&&&& 70&&&&&&&&&&&&&&&&&& 1&&&&&&&&&& 3&&&&&& 5&&&&&&&&&&&&&&&& 6&&&&&&&&&& 7用第二种方法删除所有值为2的元素从上面可以看出,删除时元素2被后面元素覆盖,不会到元素移位和内存大搬迁,残余数据留到末尾一次全部删除,也不会导致内存大搬迁,所以后者的方法要比前者在效率上好很多。&3.list容器中删除元素的方法对于list容器,由于list本身有remove和remove_if的成员函数,所以最好优先考虑list自己的算法,对于remove函数,比较简单,不再讨论,对于remove_if函数,本人发现在vc6.0中有重大问题。我试了多种函数对象,总是编译不过,通过查看源代码,才发现VC6.0中对remove_if()函数作了简化,只提供了一种比较函数,它只能删除不等于某值的元素,VC6.0种remove_if()函数的源码如下:(remove_if 之后容器大小并未改变)typedef binder2nd&not_equal_to&_Ty& & _Pr1;&&&&&& void remove_if(_Pr1 _Pr)&&&&&&&&&&&&&& {iterator _L = end();&&&&&&&&&&&&&& for (iterator _F = begin(); _F != _L; )&&&&&&&&&&&&&&&&&&&& if (_Pr(*_F))&&&&&&&&&&&&&&&&&&&&&&&&&&&& erase(_F++);&&&&&&&&&&&&&&&&&&&& else&&&&&&&&&&&&&&&&&&&&&&&&&&&& ++_F; }从源码中可以看出,remove_if中_Pr1函数对象被固定为binder2nd&not_equal_to&_Ty& &一种格式。而在VC7.0中已经修改了这个bug,源码如下:template&class _Pr1&&&&&&&&&&&&&&& void remove_if(_Pr1 _Pred)&&&&&&&&&&&&&& {&&&& // erase each element satisfying _Pr1&&&&&&&&&&&&&& iterator _Last = end();&&&&&&&&&&&&&& for (iterator _First = begin(); _First != _L )&&&&&&&&&&&&&&&&&&&& if (_Pred(*_First))&&&&&&&&&&&&&&&&&&&&&&&&&&&& erase(_First++);&&&&&&&&&&&&&&&&&&&& else&&&&&&&&&&&&&&&&&&&&&&&&&&&& ++_F&&&&&&&&&&&&&& }在VC7.0中remove_if()是成员模板函数,可以用任何判断条件的函数对象。例如:例 8:#include &iostream&#include &string&#include &list&#include &algorithm&class CTest{public:&&&&&& CTest( int i ) : m_iPrice ( i ) {&&&& }&&&&&& int operator == ( const CTest& right ) const{&&&&&&&&&&&&&& return ( m_iPrice == right.m_iPrice ) ? 1 : 0;&&&&&& }&&&&&& int operator != ( const CTest& right ) const{&&&&&&&&&&&&&& return ( m_iPrice != right.m_iPrice ) ? 1 : 0;&&&&&& }&&&&&& int operator & ( const CTest& right ) const {&&&&&&&&&&&&&& return ( m_iPrice & right.m_iPrice ) ? 1 : 0;&&&&&& }private:&&&&&& int m_iP&&&&&& friend class CTestF};class CTestFunc {&&&&&&&&&&&& //&&&&&& 函数对象public:&&&&&& int m_&&&&&& CTestFunc( int i ) : m_value( i ) {}&&&&&& bool operator () ( const CTest& clFirst ) {&&&&&&&&&&&&&& return ( clFirst.m_iPrice == m_value ) ? true :&&&& }};void main() {&&&&&& list& CTest & listT&&&&&& for ( int i = 0; i & 5; i++ ) {&&&&&&&&&&&&&& CTest clTest( i );&&&&&&&&&&&&&& listTest.push_back( clTest );&&&&&& }&&&&&& cout && "remove before : " && listTest.size() &&//&&&& 删除所有为2的元素&&&&&& listTest.remove_if( CTestFunc( 2 )&& ); //&&&&&& 这条语句在vc6.0中不能编译通过,VC7.0中可以&&&&&& cout && "remove after : 2, size =&& " && listTest.size() &&&&&&&& //&&&& 删除所以不等于2的元素,VC6.0中只能以这种方式调用remove_if()函数&&&&&& listTest.remove_if(&& bind2nd( not_equal_to&CTest&(), 2 )&&&& );&&&&&& cout && "remove after not equal to 2, size =&& " && listTest.size() &&&&&&&& //&&&& 因为CTest类提供了==、& 成员函数,所以也可以用remove函数&&&&&& listTest.remove( 2 ); //&&&&&& 删除所有为2的元素&&&&&& cout && "remove after : 2, size =&& " && listTest.size() &&}不知道在VC6.0中能否突破只能函数对象被固定为binder2nd&not_equal_to&_Ty& &一种格式的限制?欢迎诸位大虾不吝赐教。不过采用通用算法remove_if只是多了几次对象的赋值的负担,如果对象不是太大,用通用算法的性能也是可以接受的。另外,这些天使用了VC7.0后,感觉非常棒,不仅几乎符合Standard C++规范,错误提示也更清晰,而编译速度和编译后的文件大小大大减小,如我原来的一个大量使用了模板的程序,用VC6.0编译后Release版的可执行文件大小为1.2M,用VC7.0编译后只有420K,我想可能VC7.0在代码优化和模板代码的膨胀等方面有了极大的改善;在STL的实现上也有了极大的改进,把原来的一些效率不好的地方都改进了,处理策略基本与SGI STL一致。4.STL容器中元素为指针情况下的删除方法对于容器中的元素为指针的删除方法。如果容器中的元素为指针则不能用上面介绍的用通过算法或成员函数的方法删除元素,因为那样做会导致内存泄露,容器中的元素为指针指向的内存没有释放,在这种情况下有以下方法解决:1.&& 尽可能不用指针作为容器的元素。2.&& 如果是因为要减少对象拷贝和赋值方面的负担,而要在容器中存放指针的话,可以考虑用boost库中的智能指针shared_ptr包装指针,达到容器中引用的语意。3.&& 如果你不希望因为使用boost::shared_ptr增加引用计数的负担,认为引入智能指针不好理解,那么你用指针作为容器的元素要千万小心,这时你要自己管理内存。例如:&&&&例 9:用boost库中的智能指针shared_ptr包装指针的例子:#include &iostream&#include &sstream&#include &string&#include &vector&#include &algorithm&#include &list&#include &boost\smart_ptr.hpp& // 要包含BOOST类库中智能指针的头文件class CTest {public:&&&&&& CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }&&&&&& void vPrint() { cout && "name=" && m_strName && " price = " && m_iPrice &&&&&&&& }private:&&&&&& string m_strN&&&&&& int&& m_iP&&&&&& friend class CStrF&&&&&& friend class CIntF};//&&&& 函数对象,根据string比较class CStrFunc {&&&&&& string m_public:&&&&&& CStrFunc( const string& str ) : m_str( str ) {&&&&&& }&&&&&& //&&&& 此处要改为boost::shared_ptr&CTest&&,因为vector容器中的元素为//&&&&&& boost::shared_ptr&CTest&&&&&&& bool operator() ( const boost::shared_ptr&CTest&& left ) {&&&&&&&&&&&&&& return ( m_str == (*left).m_strName ) ? true :&&&&&& }};//&&&& 函数对象,根据int比较class CIntFunc {&&&&&& int m_iPpublic:&&&&&& CIntFunc( int iPrice ) : m_iPrice( iPrice ) {&&&&&& }//&&&& 此处要改为boost::shared_ptr&CTest&&,因为vector容器中的元素为//&&&&&& boost::shared_ptr&CTest&&&&&&& bool operator() ( const boost::shared_ptr&CTest&& left ) {&&&&&&&&&&&&&& return ( m_iPrice == (*left).m_iPrice ) ? true :&&&&&& }};void main( ) {&&&&&& vector& boost::shared_ptr&CTest&&& & vectT&&&&&&&&&&&& for (&& i = 0; i & 5 ; i++ ) {&&&&&&&&&&&&&&&&&&&&&&&&&&&& stream &&&&&&&&&&&&&&&& string str = stream.str();&&&&&&&&&&&&&& boost::shared_ptr&CTest&&& ptrShare( new CTest( str, i ) );&&&&&&&&&&&&&& vectTest.push_back( ptrShare );&&&&&& }&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& ( *vectTest[ i ] ).vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_strName = "3"的元素&&&&&& vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ),&&&&&&&&&&&&&& vectTest.end() );&&&&&& cout && "delete 3 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& ( *vectTest[ i ] ).vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_iPrice = 2的元素&&&&&& vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),&&&&&&&&&&&&&& vectTest.end() );&&&&&& cout && "delete 2 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& ( *vectTest[ i ] ).vPrint();&&&&&& }}以上代码不会导致内存泄露。例 10:自己编程删除容器中元素为指针的例子:#include &iostream&#include &sstream&#include &string&#include &vector&#include &algorithm&class CTest {public:&&&&&& CTest( const string& str, int iPrice ) : m_strName( str ), m_iPrice( iPrice ) { }&&&&&& void vPrint() { cout && "name=" && m_strName && " price = " && m_iPrice &&&&&&&& }private:&&&&&& string m_strN&&&&&& int&& m_iP&&&&&& //&&&& 声明友员函数,因为vDeleteVector函数要访问CTest的private成员变量&&&&&& friend void vDeleteVector( vector& CTest*&& && vectTest, const string& str );&&&&&& friend void vDeleteVector( vector& CTest*&& && vectTest, int iPrice );};//&&&& 根据CTest类中m_strName比较void vDeleteVector( vector& CTest*&& && vectTest, const string& str ) {&&&&&& vector& CTest* &::iterator itVect = vectTest.begin();&&&&&& for ( ; itVect != vectTest.end();; ) {&&&&&&&&&&&&&& if ( (*itVect)-&m_strName == str ) {&&&&&&&&&&&&&&&&&&&& //&&&&&& 删除vector容器中指针元素指向的内容,防止内存泄露&&&&&&&&&&&&&&&&&&&& delete *itV&&&&&&&&&&&&&&&&&&&& itVect = vectTest.erase( itVect );&&&&&&&&&&&&&& }&&&&&&&&&&&&&& else {&&&&&&&&&&&&&&&&&&&& ++itV&&&&&&&&&&&&&& }&&&&&& }}//&&&& 根据CTest类中m_iPrice比较void vDeleteVector( vector& CTest*&& && vectTest, int iPrice ) {&&&&&& vector& CTest* &::iterator itVect = vectTest.begin();&&&&&& for ( ; itVect != vectTest.end(); ) {&&&&&&&&&&&&&& if ( (*itVect)-&m_iPrice == iPrice ) {&&&&&&&&&&&&&&&&&&&& //&&&&&& 删除vector容器中指针元素指向的内容,防止内存泄露&&&&&&&&&&&&&&&&&&&& delete *itV&&&&&&&&&&&&&&&&&&&& itVect = vectTest.erase( itVect );&&&&&&&&&&&&&& }&&&&&&&&&&&&&& else {&&&&&&&&&&&&&&&&&&&& ++itV&&&&&&&&&&&&&& }&&&&&& }}void main( ) {&&&&&& vector& CTest*&& & vectT&&&&&&&&&&&& for (&& i = 0; i & 5 ; i++ ) {&&&&&&&&&&&&&&&&&&&&&&&&&&&& stream &&&&&&&&&&&&&&&& string str = stream.str();&&&&&&&&&&&&&& CTest* pclTest =&& new CTest( str, i ) ;&&&&&&&&&&&&&& vectTest.push_back( pclTest );&&&&&& }&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ]-&vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_strName = "5"的元素&&&&&& vDeleteVector( vectTest, "3" );&&&&&& cout && "delete 3 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ]-&vPrint();&&&&&& }&&&&&& //&&&& 删除所有m_iPrice = 2的元素&&&&&& vDeleteVector( vectTest, 2 );&&&&&& cout && "delete 2 after : " &&&&&&&& for (&& i = 0 ; i & vectTest.size(); i++ )&& {&&&&&&&&&&&&&& vectTest[ i ]-&vPrint();&&&&&& }}
阅读(...) 评论()

我要回帖

更多关于 const对象 的文章

 

随机推荐