2010-07-29 39 views
11

Tôi hiện đang làm việc trên một dự án C++ cần có ít phụ thuộc bên ngoài nhất có thể, và do đó tôi khá gắn bó với STL và Boost. Cho đến bây giờ, tôi hầu như chỉ sống trong Qt-land khi nói đến C++. Nói chung tôi có xu hướng sử dụng C# và Python khi tôi có thể.Có bao gồm thành ngữ STL để dễ đọc không?

Hôm nay tôi muốn kiểm tra xem một số std::vector có chứa một mục nhất định hay không. Với Qt, tôi sẽ làm như sau:

QList<int> list; 
list.append(1); 
list.append(2); 
list.append(3); 

if (list.contains(2)) 
{ 
    // do something 
} 

Tốt và dễ đọc. Nhưng std::vector không có phương pháp contains, điều này thật bất ngờ. Ok ... thành ngữ STL cho một thứ như thế là gì? Tìm kiếm xung quanh, có vẻ như sau:

std::vector<int> list; 
list.push_back(1); 
list.push_back(2); 
list.push_back(3); 

std::vector<int>::const_iterator result = 
    std::find(list.begin(), list.end(), 2); 

if (result != list.end()) 
{ 
    // do something 
} 

Điều đó (đối với tôi) khó đọc và quá dài dòng. Vì vậy, tôi thấy mình viết một chức năng tiện ích mà phải mất một vector và một giá trị và trả về bool tùy thuộc vào việc giá trị đã được tìm thấy hay không. Về cơ bản, một phương pháp được tạo khuôn mẫu; trình bao bọc cho cuộc gọi ở trên std::find. Sau đó tôi có thể sử dụng nó theo cách tương tự như ví dụ về Qt.

Tôi có một số chức năng tiện ích tương tự trong tâm trí mà sẽ quấn thành ngữ STL khác không có lý do khác nhưng một (nhận thức) tăng khả năng đọc. Điều tôi muốn biết là ... đây có phải là một ý tưởng tồi không? Những người khác cũng làm như vậy không? Tôi có thiếu cái gì đó quan trọng không? Mã sẽ là OSS tại một thời điểm, và tôi không muốn làm điều gì đó theo phong cách riêng mà các nhà phát triển C++ khác sẽ thấy lạ.

+1

@rgrig Điều đó đối với tôi vẫn không thể đọc được như ví dụ Qt, và như bạn đã nói, nó không hiệu quả lắm. Nó phải là O (n/2), không phải O (n). – Lucas

+0

Tôi không chắc chắn những gì bạn đang đề cập đến, vì nó dường như bị xóa, nhưng O (n/2) là O (n). Tuy nhiên, về chủ đề hiệu quả, một trình bao bọc như vậy có thể bị quá tải cho các bộ/bản đồ để tận dụng lợi thế của chúng được sắp xếp, mà không có bất kỳ thay đổi nào tại trang gọi. –

Trả lời

6

Không có gì sai khi viết các hàm tiện ích sẽ giúp bạn và sẽ làm cho mã của bạn sạch hơn. Những người khác cũng làm như vậy. Boost library là tập hợp các chức năng và lớp học tiện ích lớn nhất.

More nói C++ chuẩn một cách rõ ràng đề xuất để mở rộng bộ thư viện chuẩn (17.3.1.2/1):

Thư viện có thể được mở rộng bởi một chương trình C++. Mỗi điều khoản, nếu có, mô tả các yêu cầu mà các phần mở rộng đó phải đáp ứng. phần mở rộng như vậy nói chung là một trong các cách sau:

  • luận Template
  • lớp Derived
  • Container, vòng lặp, và/hoặc các thuật toán có thể đáp ứng một ước giao diện
3

Tôi muốn nói đó chắc chắn là một ý tưởng hay. C++ STL thiếu rất nhiều những gì các lập trình viên Python/C# đã mong đợi từ một thư viện chuẩn. Nếu bạn có thể làm cho mã của bạn dễ đọc hơn bằng cách sử dụng cách tiếp cận STL 2-3 dòng đó và làm cho nó trở thành một hàm duy nhất, hãy tiếp tục!

Đây là một ví dụ khác về vấn đề rất giống nhau: Tôi thường muốn chuyển đổi một số int thành std::string. Trước sự ngạc nhiên của tôi, không có cách nào ngắn gọn để làm điều này bằng cách sử dụng STL. Vì vậy, tôi đã viết một hàm toStr chạy 2-3 dòng cần thiết để đặt một số int vào một số stringstream và trả lại kết quả string.

Chỉnh sửa: Để làm rõ, tôi khuyên bạn nên tìm kiếm các giải pháp boost trước khi tạo của riêng bạn.Ví dụ của tôi đã được dự định để chứng minh những hạn chế của STL, nhưng có cách giải thích thay thế, "bất cứ điều gì STL là mất tích, boost có."

+5

boost có lexical_cast <>() sẽ chuyển đổi mọi thứ có thể phát trực tiếp thành chuỗi. –

+2

boost :: lexical_cast (str) cũng sẽ làm ngược lại và phân tích cú pháp chuỗi và trả về một int. – bradgonesurfing

+0

Thật vậy, đó là những gì tôi bây giờ sử dụng. Tôi vừa đưa ra một ví dụ. :) –

9

tăng cường làm cho nó trở nên thú vị hơn nhiều. Tôi không bao giờ sử dụng các thuật toán dựa trên vòng lặp STL nữa nữa. Phạm vi của các thuật toán dựa trên phạm vi là trừu tượng nhiều hơn một chút trừu tượng và dẫn đến nhiều mã sạch hơn.

#include <boost/range/algorithm/find.hpp> 

void foo(){ 
    std::vector<int> list; 
    ... 
    ... 
    boost::find(list, 2) != list.end() 
} 
1

Tương tự, trong dự án hiện tại của tôi, chúng tôi có một tệp được gọi là: stlutils.h, chứa một số phương pháp, chẳng hạn như contains(). Thực hiện như:

template<class Container, class T> 
bool contains(const Container& c, const T& value) { 
    return std::find(c.begin(), c.end(), value) != c.end(); 
} 

Hiện có nhiều chức năng, nhưng tôi nghĩ rằng bạn sẽ có được điểm

1

Những câu trả lời khác đã thực hiện các điểm mà bạn có thể viết các chức năng tiện ích để làm việc này cho bạn, và đó là một ý tưởng tốt nơi bạn cần. Nhưng tôi nghĩ rằng tôi muốn chỉ ra một điểm quan trọng: STL được thiết kế xung quanh hiệu quả thuật toán. Gần như tất cả các hoạt động với STL đều có yêu cầu hiệu suất lớn-O bắt buộc tiêu chuẩn.

Nếu vector có thành viên , chắc chắn sẽ là O (n) để gọi, vì vectơ là một danh sách tiếp giáp đơn giản. Vì nó cũng thuận tiện, nó có thể khuyến khích các lập trình viên sử dụng nó thường xuyên, ngay cả trên các tập dữ liệu lớn, khuyến khích thiết kế các ứng dụng có hiệu năng thuật toán kém. Trong trường hợp của , nếu điều quan trọng là tra cứu nếu một container chứa một phần tử nhất định, với sự bảo đảm bổ sung rằng tất cả các phần tử là duy nhất, std::set gần như chắc chắn là một lựa chọn tốt hơn, với tra cứu hiệu suất O (log n), hoặc thậm chí O (1) cho std::unordered_set.

Vì vậy, quan điểm cá nhân của tôi: tìm hiểu tất cả các vùng chứa và cung cấp các ưu đãi STL, và bạn sẽ tìm thấy trong khi nó là terse, nó khuyến khích một phong cách lập trình hiệu quả hơn. Bạn hỏi trong câu hỏi nếu bạn đang thiếu một cái gì đó, và tôi sẽ nói có - bạn muốn suy nghĩ cẩn thận hơn về các container bạn sử dụng. Tôi sử dụng set thay vì một số vector thường xuyên trong những ngày này.

1

Tôi không phải là một fan hâm mộ lớn của hàm bao, nhưng nếu họ giúp bạn ra ngoài, đi cho nó. Tôi nghĩ rằng bạn sẽ tìm thấy theo thời gian mà bạn sẽ muốn sử dụng chức năng tiện ích của bạn với các container khác ngoài std :: vector. Cuối cùng chức năng tiện ích của bạn trở nên quá chung chung mà bạn cũng có thể sử dụng std :: tìm trực tiếp.

Nhưng bạn có chắc chắn bạn đang sử dụng đúng vùng chứa không? std :: set có phương thức count(), về cơ bản tương đương với hàm contains(). Và nó là O (log (n)), chứ không phải là O (n).

Các vấn đề liên quan