2008-09-26 28 views
150

Thiết kế hệ thống mới từ đầu. Tôi sẽ sử dụng STL để lưu trữ danh sách và bản đồ của các đối tượng dài nhất định.Tôi có nên lưu trữ toàn bộ đối tượng hoặc con trỏ đến đối tượng trong vùng chứa không?

Câu hỏi: Tôi có nên đảm bảo đối tượng của mình có các nhà xây dựng bản sao và lưu trữ các đối tượng trong các thùng chứa STL hay không.

Tôi nhận ra điều này hơi ngắn về chi tiết, nhưng tôi đang tìm câu trả lời "lý thuyết" tốt hơn nếu nó tồn tại, vì tôi biết cả hai giải pháp này đều có thể.

Hai nhược điểm rất rõ ràng khi chơi với con trỏ: 1) Tôi phải tự mình quản lý/phân bổ các đối tượng này trong phạm vi ngoài STL. 2) Tôi không thể tạo đối tượng tạm thời trên ngăn xếp và thêm đối tượng đó vào vùng chứa của tôi.

Tôi còn thiếu điều gì khác không?

+31

Chúa tôi yêu trang web này, đây là câu hỏi CHÍNH XÁC tôi đã nghĩ đến hôm nay ... cảm ơn vì đã làm công việc hỏi nó cho tôi :-) – eviljack

+2

điều thú vị khác là chúng ta nên kiểm tra xem con trỏ đã được thêm vào thực sự chưa cho bộ sưu tập và nếu chúng ta không có khả năng nên gọi xóa để tránh rò rỉ bộ nhớ ... if ((set.insert (con trỏ)). second = false) {delete pointer;} – javapowered

Trả lời

61

Kể từ khi mọi người đang chiming ở trên Hiệu quả của việc sử dụng con trỏ. Nếu bạn đang xem xét sử dụng std :: vector và nếu các bản cập nhật ít và bạn thường xuyên lặp lại bộ sưu tập của mình và đó là một loại đối tượng lưu trữ không đa hình "bản sao" sẽ hiệu quả hơn vì bạn sẽ có được địa phương tốt hơn tài liệu tham khảo.

Otoh, nếu cập nhật là các con trỏ lưu trữ chung sẽ tiết kiệm chi phí sao chép/di chuyển.

+12

Điểm tốt về địa phương bộ nhớ cache. +1 – Branan

+6

Về mặt bộ nhớ cache, lưu trữ con trỏ vào vectơ có thể hiệu quả nếu được sử dụng cùng với trình phân bổ tùy chỉnh cho người được chỉ định. Trình phân bổ tùy chỉnh phải quản lý vị trí bộ nhớ cache, ví dụ như sử dụng vị trí mới (xem http://en.wikipedia.org/wiki/Placement_syntax#Custom_allocators). –

34

Nếu bạn đang lưu trữ các đối tượng đa nguyên, bạn luôn cần phải sử dụng bộ sưu tập các con trỏ cơ sở.

Đó là nếu bạn có kế hoạch lưu trữ các loại có nguồn gốc khác nhau trong bộ sưu tập của mình, bạn phải lưu trữ con trỏ hoặc bị ăn bởi deamon cắt.

+0

Tôi yêu các slice deamon! – idichekop

3

Bạn dường như nắm bắt được sự khác biệt. Nếu các đối tượng là nhỏ và dễ dàng để sao chép, sau đó bằng mọi cách lưu trữ chúng.

Nếu không, tôi sẽ nghĩ đến việc lưu trữ con trỏ thông minh (không phải auto_ptr, con trỏ thông minh đếm ref) cho những người bạn phân bổ trên heap. Rõ ràng, nếu bạn chọn con trỏ thông minh, thì bạn không thể lưu trữ các đối tượng được cấp phát tạm thời stack (như bạn đã nói).

@Torbjörn làm cho một điểm tốt về việc cắt.

+1

Ồ, và * không bao giờ * * bao giờ * * bao giờ * tạo một tập hợp các auto_ptr của –

+0

Phải, auto_ptr không phải là một con trỏ thông minh - nó không ref đếm. –

+0

auto_ptr cũng không có ngữ nghĩa sao chép không phá hủy. Hành động gán một auto_ptr từ 'one' sang' another' sẽ giải phóng tham chiếu từ 'một' và thay đổi' một'. –

17

Tại sao không tận dụng tốt nhất của cả hai thế giới: làm một container của con trỏ thông minh (như boost::shared_ptr hoặc std::shared_ptr). Bạn không cần phải quản lý bộ nhớ, và bạn không phải đối phó với các hoạt động sao chép lớn.

+0

Cách tiếp cận này khác với những gì Nick Haddad đề xuất bằng cách sử dụng Thư viện Container con trỏ tăng cường? –

+8

@ ThorstenSchöning std :: shared_ptr không thêm phụ thuộc vào tăng. –

11

Nói chung việc lưu trữ các đối tượng trực tiếp trong vùng chứa STL là tốt nhất vì nó đơn giản, hiệu quả nhất và dễ sử dụng nhất đối tượng.

Nếu đối tượng của bạn chính nó có cú pháp không copyable hoặc là một loại cơ sở trừu tượng, bạn sẽ cần phải lưu trữ con trỏ (đơn giản nhất là sử dụng shared_ptr)

+3

Nó không hiệu quả nhất nếu đối tượng của bạn lớn và bạn di chuyển các phần tử xung quanh thường xuyên. – allyourcode

44

Điều này thực sự tùy thuộc vào hoàn cảnh của bạn.

Nếu đối tượng của bạn nhỏ và làm bản sao của đối tượng có trọng lượng nhẹ, thì việc lưu trữ dữ liệu bên trong vùng chứa đơn giản và dễ quản lý hơn vì bạn không phải lo lắng về quản lý lâu dài.

Nếu bạn đối tượng lớn, và có một hàm tạo mặc định không có ý nghĩa, hoặc bản sao của đối tượng là tốn kém, sau đó lưu trữ với con trỏ có lẽ là con đường để đi.

Nếu bạn quyết định sử dụng con trỏ cho đối tượng, hãy xem Boost Pointer Container Library. Thư viện boost này kết thúc tốt đẹp tất cả các container STL để sử dụng với các đối tượng được cấp phát động.

Mỗi vùng chứa con trỏ (ví dụ ptr_vector) chiếm quyền sở hữu đối tượng khi nó được thêm vào vùng chứa và quản lý thời gian tồn tại của các đối tượng đó cho bạn. Bạn cũng có thể truy cập tất cả các phần tử trong một thùng chứa ptr_ bằng cách tham chiếu. Điều này cho phép bạn thực hiện những việc như

class BigExpensive { ... } 

// create a pointer vector 
ptr_vector<BigExpensive> bigVector; 
bigVector.push_back(new BigExpensive("Lexus", 57700)); 
bigVector.push_back(new BigExpensive("House", 15000000); 

// get a reference to the first element 
MyClass& expensiveItem = bigList[0]; 
expensiveItem.sell(); 

Các lớp này bao bọc các gói STL và làm việc với tất cả các thuật toán STL, thật sự tiện dụng.

Ngoài ra còn có các phương tiện để chuyển quyền sở hữu con trỏ trong vùng chứa cho người gọi (thông qua chức năng phát hành ở hầu hết các vùng chứa).

2

Nếu các đối tượng được chuyển đến nơi khác trong mã, hãy lưu trữ trong vectơ tăng :: shared_ptr. Điều này đảm bảo rằng các con trỏ tới đối tượng sẽ vẫn hợp lệ nếu bạn thay đổi kích cỡ vectơ.

Ie:

std::vector<boost::shared_ptr<protocol> > protocols; 
... 
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized 

Nếu cửa hàng không ai khác trỏ đến các đối tượng, hoặc danh sách không phát triển và co lại, chỉ cần lưu trữ đối tượng như đồng bằng tuổi:

std::vector<protocol> protocols; 
connection c(protocols[0]); // value-semantics, takes a copy of the protocol 
21

Xin lỗi để nhảy trong 3 năm sau sự kiện, nhưng lưu ý cảnh báo ở đây ...

Trong dự án lớn cuối cùng của tôi, cấu trúc dữ liệu trung tâm của tôi là một tập hợp các đối tượng khá đơn giản. Khoảng một năm vào dự án, khi các yêu cầu phát triển, tôi nhận ra rằng đối tượng thực sự cần phải được đa hình. Phải mất một vài tuần phẫu thuật não khó khăn và khó chịu để sửa cấu trúc dữ liệu thành một tập hợp các con trỏ lớp cơ sở, và xử lý tất cả các thiệt hại tài sản thế chấp trong lưu trữ đối tượng, đúc, và như vậy. Tôi mất một vài tháng để thuyết phục bản thân rằng mã mới đang hoạt động. Ngẫu nhiên, điều này khiến tôi suy nghĩ kỹ về mô hình đối tượng của C++ được thiết kế tốt như thế nào.

Trên dự án lớn hiện tại của tôi, cấu trúc dữ liệu trung tâm của tôi là một tập hợp các đối tượng khá đơn giản. Khoảng một năm vào dự án (điều này xảy ra vào ngày hôm nay), tôi nhận ra rằng đối tượng thực sự cần phải được đa hình. Quay lại mạng, tìm thấy chuỗi này và tìm thấy liên kết của Nick tới thư viện vùng chứa con trỏ Boost. Đây chính xác là những gì tôi phải viết lần trước để sửa mọi thứ, vì vậy tôi sẽ cho nó một lần nữa.

Về mặt đạo đức, đối với tôi, dù sao đi nữa: nếu thông số của bạn không được đúc bằng đá 100%, hãy tìm con trỏ và bạn có thể tiết kiệm cho mình nhiều công việc sau này.

+0

Thông số kỹ thuật không bao giờ được đặt trong đá. Tôi không nghĩ rằng có nghĩa là bạn nên sử dụng container con trỏ sử dụng độc quyền, mặc dù container con trỏ Boost dường như làm cho tùy chọn đó hấp dẫn hơn nhiều. Tôi nghi ngờ rằng bạn cần phải đại tu toàn bộ chương trình của bạn cùng một lúc nếu bạn quyết định rằng một container đối tượng nên được chuyển đổi thành một container con trỏ. Điều này có thể là trường hợp theo một số thiết kế. Trong trường hợp đó, nó là một thiết kế mỏng manh. Trong trường hợp đó, đừng đổ lỗi cho vấn đề của bạn về "điểm yếu" của các thùng chứa đối tượng. – allyourcode

+0

Bạn có thể đã để lại mục trong vectơ với ngữ nghĩa giá trị và đã thực hiện hành vi đa hình bên trong. –

1

Câu hỏi này đã khiến tôi lo lắng một lúc.

Tôi dựa vào lưu trữ con trỏ, nhưng tôi có một số yêu cầu bổ sung (trình bao bọc SWIG lua) có thể không áp dụng cho bạn.

Điểm quan trọng nhất trong bài này là để thử nó cho mình, sử dụng đối tượng của bạn

tôi đã làm ngày hôm nay để kiểm tra tốc độ gọi một hàm thành viên trên một bộ sưu tập của 10 triệu đối tượng, 500 lần.

Hàm cập nhật x và y dựa trên xdir và ydir (tất cả các biến thành viên float).

Tôi đã sử dụng danh sách std :: để giữ cả hai loại đối tượng và tôi nhận thấy rằng việc lưu trữ đối tượng trong danh sách nhanh hơn một chút so với sử dụng con trỏ. Mặt khác, hiệu suất là rất gần, do đó, nó đi xuống đến cách chúng sẽ được sử dụng trong ứng dụng của bạn.

Để tham khảo, với -O3 trên phần cứng của tôi, con trỏ mất 41 giây để hoàn thành và các đối tượng thô mất 30 giây để hoàn thành.

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