Tôi tự hỏi liệu có nên bao bọc các thùng chứa C++ STL để duy trì tính nhất quán và có thể hoán đổi việc triển khai mà không sửa đổi mã máy khách.Bao bì chứa đựng để duy trì tính nhất quán
Ví dụ, trong một dự án, chúng tôi sử dụng CamelCase để đặt tên lớp và các hàm thành viên (Foo :: DoSomething()), tôi sẽ quấn std :: danh vào một lớp học như thế này:
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;
};
Sau đó, tôi sẽ có thể viết một cái gì đó như thế này:
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;
}
// ...
tôi tin rằng hầu hết các compliers C++ hiện đại có thể tối ưu hóa đi những gián tiếp thêm một cách dễ dàng, và tôi nghĩ rằng phương pháp này có một số ưu điểm như:
Tôi có thể mở rộng chức năng của lớp List một cách dễ dàng. Ví dụ, tôi muốn có một chức năng tốc ký mà sắp xếp danh sách và sau đó gọi duy nhất(), tôi có thể mở rộng nó bằng cách thêm một chức năng thành viên:
template<typename T>
void List<T>::SortUnique()
{
_list.sort();
_list.unique();
}
Ngoài ra, tôi có thể trao đổi việc thực hiện cơ bản (nếu cần thiết) mà không cần bất kỳ thay đổi trên mã họ sử dụng Danh sách miễn là hành vi là như nhau. Ngoài ra còn có những lợi ích khác bởi vì nó duy trì tính nhất quán của quy ước đặt tên trong một dự án, vì vậy nó không có push_back() cho STL và PUSHBACK() cho các lớp học khác trên tất cả các dự án như:
std::list<MyObject> objects;
// insert some MyObject's.
while (!objects.empty()) {
objects.front().DoSomething();
objects.pop_front();
// Notice the inconsistency of naming conventions above.
}
// ...
Tôi tự hỏi nếu phương pháp này có bất kỳ nhược điểm lớn (hoặc nhỏ), hoặc nếu điều này thực sự là một phương pháp thực tế.
Cảm ơn.
CHỈNH SỬA: OK, cảm ơn câu trả lời cho đến nay. Tôi nghĩ rằng tôi có thể đã đặt quá nhiều vào việc đặt tên nhất quán trong câu hỏi. Trên thực tế quy ước đặt tên không phải là mối quan tâm của tôi ở đây, vì người ta có thể cung cấp một giao diện chính xác giống như tốt:
template<typename T>
void List<T>::pop_back()
{
_list.pop_back();
}
Hoặc một thậm chí có thể làm cho giao diện thực hiện khác trông giống như một STL rằng hầu hết các lập trình viên C++ đã quen thuộc với. Nhưng dù sao, theo ý kiến của tôi, đó là một điều phong cách và không quan trọng chút nào.
Điều tôi quan tâm là sự nhất quán để có thể dễ dàng thay đổi chi tiết triển khai. Một ngăn xếp có thể được thực hiện theo nhiều cách khác nhau: một mảng và một chỉ mục hàng đầu, một danh sách liên kết hoặc thậm chí là một kết hợp của cả hai, và tất cả chúng đều có đặc tính LIFO của một cấu trúc dữ liệu. Cây tìm kiếm nhị phân tự cân bằng có thể được thực hiện với cây AVL hoặc cây đỏ đen, và cả hai đều có độ phức tạp thời gian trung bình O (logn) để tìm kiếm, chèn và xóa.
Vì vậy, nếu tôi có thư viện cây AVL và một thư viện cây đỏ đen khác với các giao diện khác nhau và tôi sử dụng cây AVL để lưu trữ một số đối tượng. Sau đó, tôi đã tìm ra (bằng cách sử dụng profilers hoặc bất kỳ thứ gì), sử dụng cây đỏ đen sẽ tăng hiệu năng, tôi sẽ phải đi đến mọi phần của các tệp sử dụng cây AVL, và thay đổi tên, tên phương thức và đối số đơn đặt hàng cho các đối tác cây màu đỏ-đen của nó. Có lẽ thậm chí một số kịch bản mà lớp mới chưa có chức năng tương đương được viết. Tôi nghĩ rằng nó cũng có thể giới thiệu các lỗi tinh vi cũng vì sự khác biệt trong việc thực hiện, hoặc tôi mắc lỗi.
Vì vậy, những gì tôi bắt đầu tự hỏi rằng nếu nó là giá trị nguyên cần thiết để duy trì như một lớp bao bọc để che giấu những chi tiết thực hiện và cung cấp một giao diện thống nhất cho triển khai khác nhau:
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();
}
}
};
Bây giờ, tôi chỉ phải đảm bảo rằng AVLTree :: Find() và RBTree :: Find() thực hiện chính xác điều tương tự (ví dụ lấy giá trị cần tìm kiếm, trả về một trình lặp cho phần tử hoặc End(), nếu không). Và sau đó, nếu tôi muốn thay đổi từ một cây AVL để một cây đỏ-đen, tất cả tôi phải làm là thay đổi tuyên bố:
AVLTree<MyObject> objectTree;
AVLTree<MyObject>::Iterator it;
tới:
RBTree<MyObject> objectTree;
RBTree<MyObject>::Iterator it;
và mọi thứ khác sẽ tương tự, bằng cách duy trì hai lớp.
Ở trên chỉ là một ví dụ về những gì tôi muốn lưu trữ với lớp trình bao bọc. Có lẽ kiểu kịch bản này quá hiếm khi phát triển thực sự và tôi lo lắng quá nhiều, tôi không biết. Đó là lý do tôi hỏi.
Tôi đã thay đổi tiêu đề từ "Bao bì STL Container" thành "Bao gói để duy trì tính nhất quán" để phản ánh vấn đề chính xác hơn.
Cảm ơn thời gian của bạn.
Suy nghĩ về ý tưởng này khiến tôi bị bệnh về thể chất. –
Không thấy bất cứ điều gì sai trái khi thực hiện nó. nhưng bạn có thực sự muốn ??? – Arunmu
Vì vậy, để có được điều này thẳng, bạn muốn làm tất cả điều này chỉ vì lý do đặt tên và trao đổi việc thực hiện? Đặt tên không thể là vì bạn sẽ bọc rất nhiều thứ STL khác. – RvdK