1.往容器加入对象的时候发生拷贝
.当发生容器中元素的移动的时候(插入或删除,排序),会发生复制。
.class wiget{
Public:..
Widget(const widget &);//复制构造函数
Widget &operator=(const widget &);//复制赋值函数
};
2.当存在继承关系的时候,复制会发生剥离(sliceing)
.存放基类的容器,往其中加入派生类的对象的时候,会发生剥离,派生类对象的特有部分将会消失
Vector<widget> vw;
Class Specialwidget:public widget{....};
Specialwidget sw;
Vw.push_back(sw); ....
3.使容器动作高效,正确并防止发生剥离的一个方法是使容器包含对象指针而不是对象本身
.但是存在一些问题,比如释放的容器的时候只是释放了对象指针,而不是对象本身,会产生内存泄漏,可能auto_ptr是一个好选择。
.<如果容器包含new的对象指针,则应该手动delete掉>
void doSomething(){
vector<widget *>vw;
for (int i = 0; i < count; ++i)
vw.push_back(new widget);
....}
应当用for_each(vw.begin(),vw.end(),...);
不是异常安全的,如果delete失败的时候,则该内存不会被释放,故应该是delete成为一个函数对象:
template <typename T>
struct DeleteObject:public unary_function<const T*,void>{
void operator()(const T*ptr) const{
delete ptr;
}
};
如下:
Void dosomething{
......//与之前的相同
for_each(vw.begin(),vw.end(),DeleteObject<widget *>());
}
每次delete的时候都要指明是<widget *> 多余而繁琐。
假设有人继承了string,Specialstring,我们知道string没有虚析构函数,如果
void doSomething(){
vector<specialstring *>vw;
....
for_each(vw.begin(),vw.end(),DeleteObject<string *>());
}
//不确定的行为,通过基类指针删除派生类对象,而基类没有虚析构函数。
最终版本
struct Deleteobject
{
template<typename T>
void operator() (const T*ptr) const{
delete ptr;}
};
void doSomething(){
vector<specialstring *>vw;
....
for_each(vw.begin(),vw.end(),Deleteonject());
}
//依然不是异常安全的,如果在对象创建而for_each还没有被调用的时候发生了异常则不是异常安全的。这里用智能指针容器代替指针容器(通常是被引用计数的指针)
void doSomething(){
typedef boost::shared_ptr <widget> spw;
vector<spw> vwp;
for (int i = 0; i < count; ++i)
vw.push_back(spw(new widget));
....
}
4.调用empty而不.size来判断容器是否为空。
.通常size函数的通过线性时间来完成的。
list<int> list1;list<int> list2;
...list1.splice(
list1.end(),list2,
find(list2.begin(),list2.end(),5),
find(list2.rbegin(),list2.rend(),10).base() )//仔细思考链接过来了多少个元素。List具有把元素从一处链接到另一处而不需要复制任何数据的能力,只是需要指针的修改。通常splic为常数时间的成员函数。
5.区间成员函数优于与之对应的单元素的成员函数
.向量(vector)v1和v2,使v1的内容为v2的后半部分
<1> v1.assign( v2.begin()+v2.size()/2,v2.end() );//满分
<2> v1.clear();//比for循环的push_back好很多了。
copy( v2.begin()+v2.size()/2,v2.end(),back_inserter(v1) );
<3> vi.insert( v1.end(),v2.begin()+v2.size()/2,v2.end() ) ;
.区间创建
container::container(inputiterator begin,inputiterator end);//特例,当迭代器为istream_iterator/istreambuf_iterator的时候,会被认为的函数声明,如何避免,见下一点。
.区间插入
对于标准序列容器
void container::insert(iterator position,
inputiterator begin,inputiterator end);
对于关联容器(已经利用比较函数觉得了插入的位置)
void container::insert(inputiterator begin,inputiterator end)
.区间删除
iterator container::erase(iterator begin,iterator end);//指向删除元 素后的下一个元素//标准序列容器
void container::erase(iterator begin,iterator end);//标准就是标准 具体就是这样吧//关联容器
特别指出
对于vector和string,有一条erase并不适用,因为这种容器反复自动分配,自己管理内存,当erase后,即使元素是删除了,但是内存并没有删除,具体产出方法看第
对于区间erase,习惯用erase-remove方法来真正删除元素。
.区间赋值
void container::assign(inputiterator begin,inputiterator end);
6.C++中的分析机制
如5中所说:container::container(inputiterator begin,inputiterator end);//特例,当迭代器为istream_iterator/istreambuf_iterator的时候,会被认为的函数声明。
故:ifstream dataFile("a.txt");
list <int >data(istream_iterator<int >(dataFile),
istream_iterator<int >());//是函数声明
原因:
声明函数的3种方法;
int f(double d);//常用
int f(double (d));//即使你不知道,但是也确实是可以的
int f(double);//通常写在main函数前或者类的内部定义
声明另一个函数的3种方法:
int g(double (*pf)())//常用返回值为double和无参数的函数指针
int g(double pf())//pf用非指针的方式声明(c/c++都有效)
//pf为隐式指针
int g(double ()) // 通常参数名也可以省略
故:
list <int>data(istream_iterator<int>(dataFile),istream_iterator<int>());
//函数返回值是 list<int >;第一个是参数,()可以省略;第二个是函数指针,参数名省略,函数返回值为istream_iterator<int>,其参数为空。言不达意如class widget{..};widget w();//定义对象。
解决方法
ifstream dataFile("a.txt");
istream_iterator<int>databegin(dataFile);
istream_iterator<int>dataend;
list <int >data(databegin,dataend);
//在对data的声明中避免使用匿 名的istream_iterator对象,而是给这些迭代器一个名称。