c – 包装容器保持一致性
我想知道包装C STL容器是否保持一致性并能够交换实现而不修改客户端代码是个好主意.
例如,在一个项目中,我们使用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容器”更改为“包装容器以保持一致性”,以更准确地反映问题. 谢谢你的时间. 解决方法
两个字:维护噩梦 然后,当您获得一个新的启用移动的C 0x编译器时,您将不得不扩展所有的包装类. 不要误会我 – 如果你需要额外的功能,只是为了“一致的成员函数名称”,包装一个STL容器是没有问题的.太多的开销投入太多时间没有投资回报率. 我应该补充说:不一致的命名约定只是使用C时遇到的问题.在太多可用(和有用的)库中有太多不同的样式. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |