例如,在一个项目中,我们使用CamelCase命名类和成员函数(Foo :: DoSomething()),我将std :: list包装到类中:
template<typename T> class List { public: typedef std::list<T>::iterator Iterator; typedef std::list<T>::const_iterator ConstIterator; // more typedefs for other types. List() {} List(const List& rhs) : _list(rhs._list) {} List& operator=(const List& rhs) { _list = rhs._list; } T& Front() { return _list.front(); } const T& Front() const { return _list.front(); } void PushFront(const T& x) { _list.push_front(x); } void PopFront() { _list.pop_front(); } // replace all other member function of std::list. private: std::list<T> _list; };
然后我可以写这样的东西:
typedef uint32_t U32; List<U32> l; l.PushBack(5); l.PushBack(4); l.PopBack(); l.PushBack(7); for (List<U32>::Iterator it = l.Begin(); it != l.End(); ++it) { std::cout << *it << std::endl; } // ...
我相信大多数现代C程序员可以轻松优化掉额外的间接,我认为这种方法有一些优点,如:
我可以轻松扩展List类的功能.例如,我想要一个速记函数来排序列表,然后调用unique(),我可以通过添加一个成员函数来扩展它:
template<typename T> void List<T>::SortUnique() { _list.sort(); _list.unique(); }
此外,只要行为相同,我可以交换底层实现(如果需要),而不使用它们使用的代码的代码.还有其他好处,因为它保持了项目中命名约定的一致性,因此对于整个项目的其他类,它没有STL和PushBack()的push_back(),如下所示:
std::list<MyObject> objects; // insert some MyObject's. while ( !objects.empty() ) { objects.front().DoSomething(); objects.pop_front(); // Notice the inconsistency of naming conventions above. } // ...
我想知道这种方法是否具有任何主要(或次要的)缺点,或者这实际上是一个实际的方法.
谢谢.
编辑:
好的,谢谢你的答案.我认为我可能会把这个问题的命名一致性放在太多.实际上命名约定在这里不是我的关注,因为人们可以提供完全相同的界面:
template<typename T> void List<T>::pop_back() { _list.pop_back(); }
或者甚至可以使另一个实现的界面看起来更像是大多数C程序员已经熟悉的STL.但无论如何,在我看来,这更像是风格的东西,而不是那么重要.
我所关心的是能够轻松地改变实现细节的一致性.可以以各种方式实现堆栈:数组和顶级索引,链表,甚至两者的混合,它们都具有数据结构的LIFO特性.自平衡二叉搜索树也可以用AVL树或红黑树实现,并且它们都具有搜索,插入和删除的O(logn)平均时间复杂度.
所以如果我有一个AVL树库和另一个红黑树库有不同的接口,我使用AVL树来存储一些对象.之后,我认为(使用profilers或其他)使用红黑树将提升性能,我必须去使用AVL树的文件的每一部分,并更改类,方法名称和可能的参数命令其红黑树对应.甚至有些情况下,新的类还没有编写等效的功能.我认为也可能会因为执行方面的差异而导致微妙的错误,或者我犯了错误.
那么我开始想知道,如果保持这样一个包装类隐藏实现细节值得开销,并为不同的实现提供统一的接口:
template<typename T> class AVLTree { // ... Iterator Find(const T& val) { // Suppose the find function takes the value to be searched and an iterator // where the search begins. It returns end() if val cannot be found. return _avltree.find(val,_avltree.begin()); } }; template<typename T> class RBTree { // ... Iterator Find(const T& val) { // Suppose the red-black tree implementation does not support a find function,// so you have to iterate through all elements. // It would be a poor tree anyway in my opinion,it's just an example. auto it = _rbtree.begin(); // The iterator will iterate over the tree // in an ordered manner. while (it != _rbtree.end() && *it < val) { ++it; } if (*++it == val) { return it; } else { return _rbtree.end(); } } };
现在,我只需要确保AVLTree :: Find()和RBTree :: Find()完全相同的东西(即取值被搜索,返回一个迭代器到元素或End(),否则).然后,如果我想从AVL树更改为红黑树,我所要做的就是更改声明:
AVLTree<MyObject> objectTree; AVLTree<MyObject>::Iterator it;
至:
RBTree<MyObject> objectTree; RBTree<MyObject>::Iterator it;
一切都会一样,通过维持两个班级.
以上只是我想用包装类存档的一个例子.也许这种情况在实际发展中太少了,我担心的太多了,我不知道.这就是为什么我问.
我将标题从“包装STL容器”更改为“包装容器以保持一致性”,以更准确地反映问题.
谢谢你的时间.