2009-09-03 25 views
5

Chỉ là một câu hỏi khái niệm mà tôi đã gặp phải. Trong dự án hiện tại của tôi, có vẻ như tôi đang sử dụng quá mức các thư viện tăng smart_ptrptr_container. Tôi đã tạo boost::ptr_vectors trong nhiều đối tượng khác nhau và gọi phương thức transfer() để di chuyển một số con trỏ nhất định từ một số boost::ptr_vector sang một đối tượng khác.Nên tăng :: ptr_vector được sử dụng tại chỗ std :: vector tất cả các thời gian?

Đó là sự hiểu biết của tôi rằng điều quan trọng là phải thể hiện rõ ràng quyền sở hữu đối tượng được phân bổ heap.

Câu hỏi của tôi là, muốn sử dụng các thư viện tăng này để tạo các thành viên phân bổ heap thuộc về đối tượng nhưng sau đó sử dụng con trỏ bình thường cho các thành viên này qua get() khi thực hiện bất kỳ xử lý nào.

Ví dụ: Trò chơi có thể có một bộ sưu tập Gạch thuộc về nó. Nó có thể có ý nghĩa để tạo ra các gạch trong một boost::ptr_vector. Khi trò chơi kết thúc, các ô này sẽ tự động được giải phóng.

Tuy nhiên nếu tôi muốn đặt những gạch trong một đối tượng Bag tạm thời, tôi nên tạo một boost::ptr_vector trong túi và chuyển gạch của game đến Bag qua transfer() hoặc tôi nên tạo một nơi tham khảo std::vector<Tile*> các 's gạch * các Gạch trong Trò chơi và chuyển nó cho Túi?

Cảm ơn.

** Chỉnh sửa Tôi nên chỉ ra rằng trong ví dụ của tôi Trò chơi sẽ có đối tượng Túi làm thành viên. Túi sẽ chỉ được lấp đầy với Gạch trò chơi sở hữu. Vì vậy, túi sẽ không tồn tại mà không có trò chơi.

+2

tại sao đối tượng của bạn lại được phân bổ ở vị trí đầu tiên? Đó là nơi chuông báo động đầu tiên xuất hiện với tôi. 'ptr_vector' là một giải pháp tuyệt vời trong các trường hợp * hiếm hoi * khi bạn cần lưu trữ các con trỏ tới các đối tượng được cấp phát đống. Nhưng nếu những trường hợp đó không (rất) hiếm, thì bạn đang làm sai. ;) Nếu trò chơi có bộ sưu tập Gạch, hãy đặt * chúng * trong vectơ. Đừng đặt con trỏ vào chúng. – jalf

+1

Các câu trả lời cho câu hỏi này khá lạc hậu nếu bạn có một C++ 11 thực hiện. Ví dụ, không có lý do gì cả cho các lớp ptr_x trong C++ 11. Các thùng chứa tiêu chuẩn chỉ yêu cầu nội dung của chúng có thể di chuyển được, có nghĩa là các thùng chứa ':: std :: unique_ptr' là cách để thực hiện điều này. – Omnifarious

+0

@jalf Tôi đang bối rối. Vì vậy, nếu tôi phải điền một vector của một đối tượng phức tạp (nói từ một tập tin), tôi không nên luôn luôn phân bổ nó trên heap? Nếu tôi không phân bổ nó trong heap, đối tượng phải được sao chép và toàn bộ đối tượng sẽ được sao chép vào vectơ. Vì vậy, nếu tôi làm theo chính xác, nó thường tốt hơn để chịu chi phí của bản sao hơn để sử dụng shared_ptr, phải không? Và thông thường bạn sẽ chỉ muốn sử dụng ptr_container nếu đối tượng không thể sao chép được? – Jerahmeel

Trả lời

15

Bạn chỉ nên sử dụng con trỏ thông minh và vùng chứa con trỏ khi chuyển rõ ràng chuyển quyền sở hữu. Nó không quan trọng nếu đối tượng là tạm thời hay không - tất cả những vấn đề là liệu nó có quyền sở hữu hay không (và, do đó, cho dù chủ sở hữu trước đó từ bỏ quyền sở hữu).

Nếu bạn tạo một vector con trỏ tạm thời để chuyển nó sang một số chức năng khác và ptr_vector gốc vẫn tham chiếu đến tất cả các đối tượng đó, không có chuyển quyền sở hữu và do đó bạn nên sử dụng đối tượng tạm thời vector bạn sẽ sử dụng một con trỏ thô để truyền một đối tượng đơn lẻ từ ptr_vector đến một hàm lấy con trỏ, nhưng không lưu trữ nó ở bất kỳ đâu.

1

boost::ptr_vector chỉ phục vụ để cải thiện ngữ nghĩa của việc sử dụng vectơ của con trỏ. Nếu, trong ví dụ của bạn, mảng ban đầu có thể bị hủy trong khi tập hợp vectơ tạm thời đang được sử dụng, thì bạn chắc chắn sẽ sử dụng vectơ shared_ptr để tránh bị xóa trong khi vẫn sử dụng. Nếu không, thì một vector cũ của con trỏ có thể thích hợp. Cho dù bạn chọn std::vector hoặc boost::ptr_vector không thực sự quan trọng, ngoại trừ việc mã sử dụng véc tơ trông đẹp như thế nào.

4

Theo kinh nghiệm của tôi, có ba mẫu quyền sở hữu chính được tạo thành. Tôi sẽ gọi chúng là cây, DAG và đồ thị.

Phổ biến nhất là cây. Cha mẹ sở hữu con cái của nó, mà lần lượt sở hữu con cái của mình và như vậy. auto_ptr, scoped_ptr, con trỏ trần và các lớp ptr_x tăng là những gì bạn thường thấy ở đây. Theo tôi, con trỏ trần nói chung nên tránh khi chúng truyền đạt không có ngữ nghĩa quyền sở hữu nào cả.

Điểm phổ biến thứ hai là DAG. Điều này có nghĩa là bạn có thể có quyền sở hữu được chia sẻ. Những đứa trẻ mà cha mẹ sở hữu cũng có thể là con cái của những đứa trẻ khác mà cha mẹ sở hữu. TR1 và tăng mẫu shared_ptr là diễn viên chính ở đây. Việc đếm tham chiếu là một chiến lược khả thi khi bạn không có chu kỳ.

Phổ biến thứ ba là biểu đồ đầy đủ. Điều này có nghĩa là bạn có thể có chu kỳ. Có một số chiến lược để phá vỡ các chu kỳ đó và quay trở lại DAG với chi phí của một số nguồn lỗi có thể xảy ra. Chúng thường được biểu diễn bằng mẫu TR1 hoặc weak_ptr của boost.

Biểu đồ đầy đủ không thể được chia nhỏ thành một DAG sử dụng weak_ptr là một vấn đề không thể giải quyết dễ dàng trong C++. Các trình xử lý tốt nhất là các chương trình thu gom rác thải. Họ cũng là tổng quát nhất, có khả năng xử lý hai chương trình khác khá tốt. Nhưng tính tổng quát của họ có giá thành.

Theo ý kiến ​​của tôi, bạn không thể lạm dụng các lớp chứa ptr_x hoặc auto_ptr trừ khi bạn thực sự nên sử dụng các thùng chứa các đối tượng thay vì các thùng chứa con trỏ. shared_ptr có thể bị lạm dụng. Hãy suy nghĩ cẩn thận về việc bạn có thực sự cần một DAG hay không.

Tất nhiên tôi nghĩ mọi người chỉ nên sử dụng các vùng chứa scope_ptrs thay vì tăng các lớp ptr_x, nhưng điều đó sẽ phải chờ C++ 0x. :-(

1

Trong ví dụ về gạch trò chơi của bạn, bản năng đầu tiên của tôi là tạo vector<Tile>, không phải là vector<Tile*> hoặc ptr_vector. Sau đó, sử dụng con trỏ vào ô xếp theo ý bạn, mà không lo lắng về quyền sở hữu, miễn là các đối tượng giữ con trỏ không sống lâu hơn vectơ của các viên gạch Bạn đã nói rằng nó chỉ bị phá hủy khi trò chơi kết thúc, do đó điều đó không khó.

Tất nhiên có thể có những lý do không thể, cho Ví dụ vì Tile là một lớp cơ sở đa hình, và bản thân các tile không phải là tất cả cùng một lớp, nhưng đây là C++, không phải Java, và không phải mọi vấn đề luôn cần đa hình động, nhưng ngay cả khi bạn thực sự cần các con trỏ, bạn vẫn có thể tạo bản sao của những con trỏ đó mà không cần bất kỳ ngữ nghĩa sở hữu, với điều kiện quy mô và lưu trữ thời gian của đối tượng chỉ để được hiểu là rộng hơn so với phạm vi và thời hạn sử dụng của con trỏ:

int main() { 
    vector<Tile*> v; 
    // fill in v, perhaps with heap objects, perhaps with stack objects. 
    runGame(v); 
} 

void runGame(const vector<Tile*> &v) { 
    Tile *firsttile = v[0]; 
    vector<Tile*> eventiles; 
    eventiles.push_back(v[2]); 
    eventiles.push_back(v[4]); 
    // and so on. No need to worry about ownership, 
    // just as long as I don't keep any pointers beyond return. 
    // It's my caller's problem to track and free heap objects, if any. 
} 
0

Nếu gạch được đặt trong túi và ai đó đánh cắp Bag bạn mất tất cả các gạch. Do đó, Gạch, mặc dù tạm thời, nhưng chúng thuộc về cho Túi trong một thời gian ngắn. Bạn nên chuyển quyền sở hữu ở đây, tôi cho là vậy.

Nhưng ý kiến ​​hay hơn sẽ không gây rối với quyền sở hữu, bởi vì tôi không thấy lý do tại sao cần thiết trong trường hợp cụ thể này. Nếu có điều gì đó đằng sau hiện trường, cứ tiếp tục, hãy lựa chọn.

2

Nhiều khả năng, giải pháp bạn đang muốn tìm

std::vector<Tile> 

Không có cần cho con trỏ hầu hết thời gian. Các vector đã chăm sóc bộ nhớ quản lý của các đối tượng chứa. Các viên gạch được sở hữu bởi các trò chơi, phải không? Cách để làm rõ điều đó là đặt chính các đối tượng trong lớp trò chơi - các trường hợp duy nhất mà bạn thường cần con trỏ và phân bổ động các đối tượng riêng lẻ là nếu bạn cần 1) đa hình hoặc 2) quyền sở hữu được chia sẻ.

Nhưng con trỏ phải là ngoại lệ, không phải quy tắc.

1

Nếu Trò chơi sở hữu các ô thì Trò chơi sẽ có thể tuân theo do có sự cố.

Nghe có vẻ như túi không bao giờ thực sự sở hữu các đối tượng nên không được tuân thủ để xóa chúng. Vì vậy, tôi sẽ sử dụng ptr_vector trong đối tượng Game. Nhưng sử dụng một std :: vector trong túi.

Lưu ý: Tôi sẽ không bao giờ cho phép bất kỳ ai sử dụng túi lấy con trỏ đến một ô từ túi. Họ chỉ có thể lấy một referenceto gạch từ túi.

0

Tôi đồng ý rằng việc sử dụng véc tơ thay vì nhảy ngay vào phân bổ T được phân bổ là một bản năng tốt, nhưng tôi có thể dễ dàng thấy Ngói là một loại trong khi xây dựng bản sao đắt tiền. Tất nhiên, điều đó có thể được giải quyết tốt nhất với một danh sách, không phải là một vector ...

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