Hãy chia nhỏ những tuyên bố này thành những hiện tượng có thể đo lường được:
- Nhẹ hơn: Container Qt sử dụng ít bộ nhớ hơn container STL
- An toàn hơn: Container Qt có ít cơ hội được sử dụng không đúng cách
- Dễ dàng hơn: Các thùng chứa Qt thể hiện ít gánh nặng trí tuệ hơn
Dễ dàng hơn
Khiếu nại được đưa ra trong ngữ cảnh này là việc lặp theo kiểu java bằng cách nào đó "dễ dàng" hơn kiểu STL, và do đó Qt dễ sử dụng hơn do giao diện bổ sung này.
Phong cách Java:
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
Phong cách STL:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
Kiểu trình lặp Java có lợi ích là nhỏ hơn và sạch hơn một chút. Vấn đề là, đây không thực sự là phong cách STL nữa.
Phong cách C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
hoặc là
Phong cách foreach C ++ 11
for (QString i : list)
qDebug << i;
Điều này đơn giản đến mức không có lý do gì để sử dụng bất cứ thứ gì khác (trừ khi bạn không hỗ trợ C ++ 11).
Tuy nhiên, yêu thích của tôi là:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Vì vậy, như chúng ta có thể thấy, giao diện này không mang lại cho chúng ta điều gì ngoại trừ một giao diện bổ sung, bên trên một giao diện hiện đại, đẹp mắt và hợp lý. Thêm một mức độ trừu tượng không cần thiết trên giao diện đã ổn định và có thể sử dụng? Không phải ý tưởng của tôi về "dễ dàng hơn".
Ngoài ra, giao diện Qt foreach và java thêm chi phí; họ sao chép cấu trúc, và cung cấp một mức độ không cần thiết. Điều này có vẻ không nhiều, nhưng tại sao lại thêm một lớp chi phí để cung cấp giao diện không đơn giản hơn nhiều? Java có giao diện này vì java không có quá tải toán tử; C ++ nào.
An toàn hơn
Sự biện minh mà Qt đưa ra là vấn đề chia sẻ ngầm, không phải là vấn đề ngầm hay vấn đề. Nó không liên quan đến việc chia sẻ, tuy nhiên.
QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.
QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/
Đầu tiên, đây không phải là ẩn ý; bạn rõ ràng đang gán một vectơ cho một vectơ khác. Đặc tả trình lặp STL chỉ rõ rằng các trình vòng lặp thuộc về vùng chứa, vì vậy chúng tôi đã giới thiệu rõ ràng một vùng chứa chung giữa b và a. Thứ hai, đây không phải là một vấn đề; miễn là tất cả các quy tắc của đặc tả iterator được tuân theo, hoàn toàn không có gì sai. Lần duy nhất có sự cố xảy ra là ở đây:
b.clear(); // Now the iterator i is completely invalid.
Qt chỉ định điều này như thể nó có ý nghĩa gì đó, giống như một vấn đề phát sinh từ kịch bản này. Nó không. Trình lặp không hợp lệ và giống như bất kỳ thứ gì có thể được truy cập từ nhiều vùng khác nhau, đây chỉ là cách nó hoạt động. Trên thực tế, điều này sẽ xảy ra dễ dàng với các trình vòng lặp kiểu Java trong Qt, nhờ nó phụ thuộc rất nhiều vào việc chia sẻ ngầm, đó là một phản đề như được ghi lại ở đây và tại nhiều khu vực khác . Điều này có vẻ đặc biệt kỳ lạ đối với việc "tối ưu hóa" này được đưa vào sử dụng trong một khuôn khổ ngày càng hướng tới đa luồng, nhưng đó là tiếp thị cho bạn.
Bật lửa
Đây là một chút phức tạp hơn. Việc sử dụng các chiến lược tăng trưởng và chia sẻ sao chép và ghi lại tiềm ẩn khiến cho việc thực sự đảm bảo về việc bộ nhớ của bạn sẽ sử dụng bao nhiêu bộ nhớ tại bất kỳ thời điểm nào. Điều này không giống như STL, mang đến cho bạn sự đảm bảo về thuật toán mạnh mẽ.
Chúng ta biết giới hạn tối thiểu của không gian bị lãng phí cho một vectơ là căn bậc hai của chiều dài của vectơ , nhưng dường như không có cách nào để thực hiện điều này trong Qt; các "tối ưu hóa" khác nhau mà họ hỗ trợ sẽ loại trừ tính năng tiết kiệm không gian rất quan trọng này. STL không yêu cầu tính năng này (và hầu hết sử dụng tăng trưởng gấp đôi, sẽ lãng phí hơn), nhưng điều quan trọng cần lưu ý là ít nhất bạn có thể thực hiện tính năng này, nếu cần.
Điều tương tự cũng đúng với các danh sách được liên kết đôi, có thể sử dụng liên kết XOr để giảm đáng kể dung lượng sử dụng. Một lần nữa, điều này là không thể với Qt, do yêu cầu của nó đối với tăng trưởng và COW.
COW thực sự có thể làm cho thứ gì đó nhẹ hơn, nhưng các Intrusive Container cũng có thể, như được hỗ trợ bởi boost và Qt đã sử dụng chúng thường xuyên trong các phiên bản trước, nhưng chúng không được sử dụng nhiều nữa vì chúng khó sử dụng, không an toàn và gây gánh nặng về lập trình viên. COW là một giải pháp ít xâm phạm hơn, nhưng không hấp dẫn vì những lý do nêu trên.
Không có lý do tại sao bạn không thể sử dụng các bộ chứa STL với cùng chi phí bộ nhớ hoặc ít hơn bộ chứa của Qt, với lợi ích bổ sung là thực sự biết bạn sẽ lãng phí bao nhiêu bộ nhớ tại bất kỳ thời điểm nào. Thật không may, không thể so sánh hai cách sử dụng bộ nhớ thô, bởi vì các điểm chuẩn như vậy sẽ cho thấy kết quả rất khác nhau trong các trường hợp sử dụng khác nhau, đây là loại vấn đề chính xác mà STL được thiết kế để khắc phục.
Tóm lại là
Tránh sử dụng Bộ chứa Qt khi có thể làm như vậy mà không áp dụng chi phí sao chép và sử dụng phép lặp loại STL (có thể thông qua trình bao bọc hoặc cú pháp mới), bất cứ khi nào có thể.