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;
}
}

浙公网安备 33010602011771号