Effective STL第2条:不要试图编写独立于容器类型的代码(容器封装)

一、STL是一套泛型编程

  • STL是以泛化原则为基础的:
    • 数组被泛化为“以其包含的对象的类型为参数”的容器
    • 函数被泛化为“以其使用的迭代器的类型为参数”的算法
    • 指针被泛化为“以其指向的对象的类型为参数”的迭代器
  • 容器类型被泛化为序列容器和关联容器。但是每种不同的容器所具备的特性是不同的
  • 随着泛化的不断进行,有些人可能会视图编写独立于容器类型的代码,出发点是好的,但是会误入歧途,下面我们慢慢介绍

二、不要试图编写独立于容器类型的代码

  • 视图编写对序列容器和关联容器都适用的代码是毫无意义的:
    • 1.很多成员函数仅当其容器为某一种类型时才存在,不同的容器类型拥有不同的函数操作方法。例如只有序列容器才支持push_front或push_back,只有关联容器才支持count和lower_bound
    • 2.根据1如果想要编写所有容器都适用的代码,那么必须取所有容器功能的交集,而舍弃各个容器独有的操作
    • 3.限制的根源在于:不同类型的序列容器,使迭代器、指针和引用失效的规则是不同的
    • 4.移植性:不是所有容器都可以把数据传递到C接口中,只有vector才可以(见第16条)
    • 5.不能使用bool作为要存储的对象类型来实例化容器(见第18条)

三、程序优化之类型封装

  • 编程中,需要考虑从另一种容器转换到另一种容器时,可以使用“封装”技术来简化编程,最常用的方法是通过对容器类型和其迭代器类型使用类型定义(typedef)

演示案例

  •  如果我们有下面一个案例
class Widget {};

vector<Widget> vw;
Widget bestWidget;

//为bestWidget赋值

//找到一个与bestWidget具有同样的值的Widget
vector<Widget>::iterator i = find(vw.begin(), vw.end(), bestWidget);
  • 比较好的方式是改写为以下的代码格式
class Widget {};

//类型定义
typedef vector<Widget> WidgetContainter;
typedef vector<Widget>::iterator WCIterator;

WidgetContainter vw;
Widget bestWidget;

WCIterator i = find(vw.begin(), vw.end(), bestWidget);
  •  通过这样的修改,当我们需要改变容器类型的时候就容易多了,只需要该typedef处的代码就可以了,而不需要修改整个程序

四、程序优化之类封装

  • 上面我们介绍了类型定义封装对程序的方便性。当然,如果你不想把自己选择的容器暴露给用户使用,那么就可以考虑使用类封装,并尽量减少可以供外部访问的类的接口(含有容器的相关信息)

演示案例

  • 例如,想要创建一个顾客列表,那么可以不直接使用list,而是将list封装到一个类中
class Customer {};

class CustomerList
{
private:
	typedef list<Customer> CustomerContainer;
	typedef list<Customer>::iterator CCIterator;

	CustomerContainer customers;
public:
	//精良减少那些通过接口可见的、并且与list相关的信息
};
  • 如果在后面进行编程的时候你可能不需要频繁地在列表的中间插入或者删除顾客,而是快速确定最前面的20%的顾客,那么你就可以使用nth_element算法完成(见第31条),可是nth_element要求随机访问迭代器,但是list不支持。所以在这个时候你可能就需要更换list为vector或deque来实现,更换的时候只需要更改类就可以,而不需要更改整个程序,因此简化了编程
  • 另外,可能还需要考虑CustomerList的每个成员函数和友元等等,看看它们如何受到影响(根据性能和使迭代器/指针/引用无效的规则,等等),因此,我们是无法编写独立于容器类型的代码的
发布了1313 篇原创文章 · 获赞 834 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/103944948