2012-11-07 42 views
5

Đối với trường hợp như vậy:khởi động đúng cách thông minh con trỏ mảng

class A 
    { 
     //implementation 
    }; 

    class B 
    { 
    public: 
     B(); 
     ~B(); 
    private: 
     std::vector<std::shared_ptr<A>> _innerArray; 
    }; 

tôi nên làm gì trong B() để tạo ra một đối tượng với trạng thái hợp lệ? Tôi có cần phải tự gọi hàm khởi tạo mặc định cho mỗi đối tượng A trong mảng không? Và tôi có cần phải làm điều gì đó đặc biệt trong ~B() không? Nếu lớp B là ví dụ về thiết kế xấu, hãy nói cách làm cho nó tốt hơn. Cảm ơn.

Chỉnh sửa Vì vậy, đây là sơ đồ về những gì tôi thực sự cần ở đây.

enter image description here

giá trị Vì vậy, thực được lưu trữ chỉ trong mảng của A và tất cả các đối tượng khác là để lưu trữ các kết nối. Ví dụ đơn giản nhất - A = dấu chấm, B = Đường thẳng (hoặc đường cong) đi qua các chấm được chọn và C = mặt phẳng được mô tả bằng các dòng. Hy vọng nó làm cho câu hỏi chính xác hơn.

+1

Kiểm tra tính chất: bạn có cần con trỏ ở đây không? Và nếu vậy, nó có nên là một con trỏ được chia sẻ không? –

+0

Lớp B được cho là có quyền truy cập nhưng không sở hữu một số đối tượng được lưu trữ ở nơi khác. Tôi không thấy sử dụng unique_ptr thay vì shared_ptr làm cho bất kỳ ý nghĩa ở đây, nhưng tôi là một noob để con trỏ thông minh. –

+0

@Pavel Bạn nói đúng: kịch bản của bạn ngăn cản việc sử dụng 'unique_ptr', và nó yêu cầu sử dụng con trỏ. Tuy nhiên, nếu 'B' không được coi là sở hữu, tôi thậm chí còn xem xét sử dụng con trỏ thô thay vì con trỏ được chia sẻ. Xét cho cùng, khái niệm sau biểu thị * quyền sở hữu được chia sẻ * (nhưng có thể đó là những gì bạn yêu cầu sau khi tất cả). –

Trả lời

4

Để tạo đối tượng B ở trạng thái hợp lệ, bạn không phải làm gì thêm nữa. Bạn thậm chí không phải khai báo và triển khai hàm tạo và hàm hủy cho B. std::vector<std::shared_ptr<A>> là thành viên của B sẽ được khởi tạo mặc định trong hàm tạo của B có nghĩa là nó sẽ không có bất kỳ phần tử nào trong vùng chứa. Nó cũng sẽ bị xóa một cách chính xác trong ~B nhờ vào số std::vectorstd::shared_ptr trình phá hủy.

Mặt khác, nếu bạn muốn khởi tạo bằng cách nào đó (ví dụ:3 giá trị), bạn có thể sử dụng hàm tạo 's std::initializer_list trong danh sách khởi tạo hàm khởi tạo của B. Ví dụ:

class B 
{ 
public: 
    B(): _innerArray{ std::make_shared<A>(), 
         std::make_shared<A>(), 
         std::make_shared<A>() } {} 
    ~B() {} 
private: 
    std::vector<std::shared_ptr<A>> _innerArray; 
}; 

Hãy nhớ rằng std::make_shared sử dụng chuyển tiếp hoàn hảo để bạn vượt qua A đối số của constructor như các đối số chức năng và không phải là đối tượng lớp riêng của mình.

Trả lời các thắc mắc của bạn về thiết kế Tôi muốn khuyến khích bạn trước tiên nghĩ về quyền sở hữu độc quyền của các thành viên trong một véc tơ trước khi bạn quyết định chia sẻ chúng.

class B 
{ 
public: 
    B(); 
    ~B(); 
private: 
    std::vector<std::unique_ptr<A>> _innerArray; 
}; 

Thực hiện ở trên hiệu quả hơn trên nhiều cơ sở. Trước hết nó làm cho thiết kế của bạn rõ ràng hơn về ai chịu trách nhiệm về tuổi thọ của A s. Tiếp theo std::unique_ptr nhanh hơn vì nó không yêu cầu tính tham chiếu an toàn chỉ. Và cuối cùng nhưng không kém phần chi phí bộ nhớ bổ sung (so với con trỏ C thông thường) trong khi std::shared_ptr có thể mất hàng chục byte (24-48) để lưu trữ dữ liệu trạng thái được chia sẻ rất hiệu quả khi bạn hoạt động trên các lớp nhỏ. Đó là lý do tại sao tôi luôn sử dụng std::unique_ptr làm con trỏ thông minh đầu tiên của mình và tôi chỉ dự phòng số std::shared_ptr khi thực sự cần thiết.

EDIT:

Answering chỉnh sửa của bạn tôi sẽ tạo ra 3 container của các tầng lớp A, B, C. Tùy thuộc vào thực tế nếu bạn cần họ để được đa hình hay không tôi sẽ lưu trữ một trong hai giá trị như thế (loại không đa hình):

std::deque<A> as; 
std::deque<B> bs; 
std::deque<C> cs; 

hoặc (loại đa hình):

std::vector<std::unique_ptr<A>> as; 
std::vector<std::unique_ptr<B>> bs; 
std::vector<std::unique_ptr<C>> cs; 

theo thứ tự (as phải dài hơn bsbs phải dài hơn cs). Sau đó, tôi chỉ có std::vector<A*> bên trong lớp Bstd::vector<B*> bên trong C lớp mà không cần sử dụng con trỏ thông minh nào.

Tôi hy vọng điều đó sẽ hữu ích.

EDIT:

Changed std::vector để std::deque trong trường hợp đầu tiên cho phép tài liệu tham khảo/con trỏ đến các yếu tố chứa tồn container mở rộng với push_back(). Tuy nhiên, chúng sẽ không tồn tại các yếu tố tẩy xoá, phân loại hoặc các thứ khác.

+0

Câu trả lời hay. Hoàn hảo. –

2

Nếu bạn làm như vậy, vectơ có kích thước bằng 0 phần tử, tức là nội dung được khởi tạo đúng cách một cách bình thường. Nếu vectơ có kích thước dương (ví dụ: sau khi gọi resize trên vectơ), mỗi phần tử sẽ được khởi chạy đúng cách. Vì các phần tử là shared_ptr s, hàm khởi tạo mặc định của shared_ptr sẽ được gọi, có nghĩa là bạn sẽ kết thúc với một vectơ của các con trỏ trống.

Nếu bạn muốn sao chép nội dung từ thùng chứa khác, sử dụng phiên bản iterator của các nhà xây dựng vector:

B (SomeContainerTypeContainingSharedPointers container) 
: _innerArray (container.begin(), container.end()) { 
} 

Nếu bạn không muốn khởi tạo vector từ một container, nhưng từ một nơi khác (ví dụ như tạo các đối tượng khi đang di chuyển) - tự viết một loại biến lặp đầu vào (tức là loại "trình vòng lặp nhà máy").

1

Vectơ trống nên bạn không phải làm gì đặc biệt trong hàm tạo mặc định. Và bạn cũng không cần phải làm bất cứ điều gì trong số B(). Số tham chiếu của shared_ptrs sẽ tự động bị giảm khi hàm hủy của vectơ được gọi.

1

Mặc định Bt std::shared_ptr<A> sẽ điền vào ptr bên trong với NULL. Để tạo con trỏ thông minh sử dụng std::make_shared:

_innerArray.push_back(std::make_shared<A>(/*constructor params here*/)); 

Nhưng trong ví dụ vectơ của bạn trống.

0

Trình tạo mặc định đã thực hiện mọi thứ cần thiết. Bạn thậm chí có thể để lại B() mà không bị mất.

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