Effective STL 学习关于容器

一、容器的选择

  标准STL序列容器:vector,string,deque,list。其中前三种是连续内存容器,list是节点内存容器(非连续内存)

  序列容器特点:任意位置删除、插入元素;插入、删除时元素会移动;元素是有顺序的,可以随机访问(list除外),vector数据内存兼容C,string支持引用计数,list支持多元素插入事务性语义(连续内存容器也支持但是性能差);基于节点的非删除迭代器指向元素,迭代器不会失效。

  标准STL关联容器:set,mutiset,map,mutimap。

二、编写容器无关代码

  1. 使用typedef 如:std::vector<int> 可以写成 typedef std::vector<int> IntContainer;

  2. 当容器需要依赖特定方法时,可以对容器就行封装,提供统一的方法(性能稍微有点损耗);

    如:

class Container
{
    public:
        size_t Size();
        bool HasNext();
        int Next();
        ........
    private:    
        vector<int> m_vec;
}            

三、使容器里对象的拷贝轻量且正确

  std::vector<Object> vec;

  vec.push_back(obj);  //会调用Object的拷贝构造函数对元素就行拷贝插入容器中

  1.如果上面Object对象比较大,插入元素的代价是比较大的,此时容器应该存对象指针即定义为:std::vector<Object *> vec;担心元素内存释放可用smart_ptr智能指针。

  2.当Object是基类时拷贝可能导致累被分割。

四、用empty代替size()检查容器是否为空

  1.splice函数和size函数谁是常数时间函数和线性函数时间的取舍问题。size可能是线性时间实现,但empty能肯定是常数时间函数。

五、尽量用区间成员函数代替单元素函数

  1.使用区间成员函数:assign,insert ,erase。。。,可以避免手写循环,简化程序并且使程序更直观

    如:insert(开始插入位置iterator,插入区间起点,插入区间终点);

六、读取固定格式文件的一种方便实现

 ints.dat存放的是一组int值时可以这样读文件

ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin, dataEnd);

//上面代码不能这样实现
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());//并不是所以编译器都会认

七、一个错误:

  class Widget {...}; // 假设Widget有默认构造函数
  Widget w(); //编译器会认为是定义了一个不带参数的w函数,返回值为Widget,而不是认为是定义了一个Widget变量w。  

八、用for_each和智能指针,管理指针容器中new的元素

  1.for_each

std::vector<Object *> vec;

struct DeleteObject
{
    template<typename T>
    void operator()(const T* ptr) const 
    { 
        delete ptr;
        ptr = NULL;
    } 
};
...//插入元素操作
for_each(vec.begin(), vec.end(), DeleteObject()); //删除元素

  2.智能指针

typedef shared_ptr<Object> ShareObj;

vector<ShareObj> vec;

vec.push_back(ShareObj(new Object));

//注意不要用auto_ptr,auto_ptr会转移对象拥有权
//原因:
对象转移:
auto_ptr<Object> obj1(new Object); //obj1指向一个Object
auto_ptr<Object> obj2(obj1);    //obj1指向的对象转移给obj2,obj1指向NULL
obj1 = obj2;             //obj1指向Object,obj2指向NULL
进而做以下操作可能发生错误:
auto_ptr<Object> obj = vec[i]; //如果做此操作会使vector中第i个元素对象指向NULL,很容易出现你不想要的结果。

九、不同容器删除元素的方法:

  1. vector,string,deque

Container<int> c;  //容器为vector,string,deque
c.erase(remove(c.begin(), c.end(), 1963), c.end());   //erase remove 惯用法  

  2.list

list<int> c;  
c.remove(1123);     

  3.set, mutiset, map, mutimap等关联容器

set<int> c;
c.erase(1212);

  4.删除容器中有特定值的元素

bool badValue(int val);

Container<int> c
c.erase(remove_if(c.begin(), c.end(), badValue), c.end()); //vector,string,deque

c.remove_if(badValue);                          //list


//关联容器
for (Container<int>::iterator i = c.begin(); i != c.end(); /*nothing*/ )
{
    if (badValue(*i)) 
       c.erase(i++);         // 对于坏的值,把当前的
    else ++i;                   // i传给erase,然后
}                                   // 作为副作用增加i;

//非关联容器还可以用remove_copy_if函数,先把不删除的的元素拷贝到新的容器,然后把新容器通过swap函数交换到旧容器中
//非关联容器用循环的方法 for (Container<int>::iterator i = c.begin(); i != c.end(); ) { if (badValue(*i)) { i = c.erase(i); // 通过把erase的返回值 } // 赋给i来保持i有效 else { ++i; } }

  

  

  

发布选项

原文地址:https://www.cnblogs.com/4tian/p/2874103.html