2009-07-17 30 views
8

Tôi có lớp foo có chứa một thành viên std :: auto_ptr mà tôi muốn sao chép cấu trúc nhưng điều này dường như không được phép. Có một điều tương tự cho nhiệm vụ. Xem ví dụ sau:Làm thế nào để sử dụng một std :: auto_ptr trong một lớp học bạn phải sao chép xây dựng?

struct foo 
{ 
private: 
    int _a; 
    std::string _b; 
    std::auto_ptr<bar> _c; 

public: 
    foo(const foo& rhs) 
     : _a(rhs._a) 
     , _b(rhs._b) 

     , _c(rhs._c) 
       // error: Cannot mutate rhs._c to give up ownership - D'Oh! 
    { 
    } 

    foo& operator=(const foo& rhs) 
    { 
     _a = rhs._a; 
     _b = rhs._b; 

     _c = rhs._c; 
      // error: Same problem again. 
    } 
}; 

Tôi chỉ có thể tuyên bố _c như mutable nhưng tôi không chắc chắn điều này là đúng. Có ai có một giải pháp tốt hơn?

EDIT

OK, tôi không nhận được các loại câu trả lời mà tôi đã mong đợi vì vậy tôi sẽ có một chút cụ thể hơn về vấn đề này.

  • Một đối tượng kiểu foo được tạo trên ngăn xếp và được chuyển theo giá trị vào một lớp chứa (không phải stl) và sau đó nằm ngoài phạm vi. Tôi không có quyền kiểm soát mã vùng chứa. (Nó thực sự là một hoạt động xếp hàng đang hoạt động, có lỗi.)
  • Lớp bar là một trình phân tích cú pháp khá nặng. Nó có hiệu suất rất kém trên newdelete vì vậy ngay cả khi nó là bản sao có thể xây dựng, nó sẽ là quá đắt.
  • Chúng tôi có thể đảm bảo rằng khi đối tượng bar được tạo, nó sẽ chỉ cần được sở hữu ở 1 địa điểm cùng một lúc. Trong trường hợp này nó đang được truyền giữa các luồng và bị xóa khi giao dịch được hoàn tất. Đây là lý do tại sao tôi đã hy vọng sử dụng một std::autp_ptr.
  • Tôi rất sẵn lòng cân nhắc việc tăng các con trỏ thông minh nhưng tôi hy vọng sẽ đảm bảo tính duy nhất này nếu có sự thay thế.

Trả lời

16

Bạn có thể muốn thử đoạn mã sau:

foo(const foo& rhs) 
     : _a(rhs._a) 
     , _b(rhs._b) 
     , _c(_rhs._c.get() ? new bar(*_rhs._c.get()) : 0) 
    { 
    } 

(toán tử gán là tương tự.)

Tuy nhiên điều này sẽ chỉ làm việc nếu bar là CopyConstructible và nếu điều này thực sự làm những gì bạn muốn. Vấn đề là cả hai đối tượng foo (_rhs và được xây dựng) sẽ có các con trỏ khác nhau trong _c.

Nếu bạn muốn họ chia sẻ con trỏ thì bạn không được sử dụng auto_ptr vì nó không hỗ trợ quyền sở hữu được chia sẻ. Hãy xem xét trong trường hợp sử dụng ví dụ như shared_ptr từ Boost.SmartPtr ví dụ (sẽ được bao gồm trong tiêu chuẩn C++ mới). Hoặc bất kỳ việc thực hiện con trỏ chia sẻ nào khác vì đây là một khái niệm phổ biến mà rất nhiều triển khai có sẵn.

+0

Vẫn là câu trả lời hay nhất ngay cả sau khi chỉnh sửa. foo sẽ cần phải sao chép hoặc chia sẻ thanh và auto_ptr không chia sẻ tốt. Vì việc sao chép hiện bị loại trừ, bạn sẽ cần shared_ptr. – MSalters

8

Như bạn đã phát hiện bạn không thể sao chép std::auto_ptr như thế. Sau khi bản sao sở hữu đối tượng được trỏ đến? Thay vào đó, bạn nên sử dụng một con trỏ thông minh được tính tham chiếu. Boost libraryshared_ptr bạn có thể sử dụng.

0

Toàn bộ ý tưởng của auto_ptr là chỉ có một chủ sở hữu của đối tượng được đề cập. Điều này ngụ ý rằng bạn không thể sao chép con trỏ mà không xóa quyền sở hữu ban đầu.

Vì bạn không thể sao chép nó, bạn cũng không thể sao chép đối tượng chứa an auto_ptr.

Bạn có thể thử sử dụng di chuyển ngữ nghĩa học, ví dụ: sử dụng std::swap thay vì sao chép.

+0

-1, nó là tầm thường để sao chép một đối tượng có chứa một auto_ptr . Bạn làm như vậy bằng cách tạo một auto_ptr mới trỏ đến một bản sao T. – MSalters

+0

@MSalters: nhưng sau đó bạn không sao chép auto_ptr. Bạn đang sao chép đối tượng được gọi. – xtofl

3

Trước tiên, tôi muốn tránh auto_ptr Chuyển

sở hữu là tốt trong một số tình huống, nhưng tôi thấy họ rất hiếm, và "đầy đủ" các thư viện con trỏ thông minh hiện nay có sẵn một cách dễ dàng. (IIRC auto_ptr là một thỏa hiệp để bao gồm ít nhất một ví dụ trong thư viện chuẩn, mà không có sự chậm trễ mà một thực hiện tốt sẽ có yêu cầu).

See, for example here
or here

Quyết định về ngữ nghĩa
nên các bản sao của foo giữ một tham chiếu đến cùng một ví dụ của thanh? Trong trường hợp đó, hãy sử dụng boost::shared_ptr hoặc (boost::intrusive_ptr) hoặc thư viện tương tự.

Hoặc nên tạo bản sao sâu? (Đôi khi có thể được yêu cầu, ví dụ: khi có trạng thái tạo chậm). Tôi không biết thực hiện tiêu chuẩn nào về khái niệm đó, nhưng nó không phức tạp để xây dựng tương tự như các con trỏ thông minh hiện có.

// roughly, incomplete, probably broken: 
    template <typename T> 
    class deep_copy_ptr 
    { 
     T * p; 
    public: 
     deep_copy_ptr() : p(0) {} 
     deep_copy_ptr(T * p_) : p(p_) {} 
     deep_copy_ptr(deep_copy_ptr<T> const & rhs) 
     { 
     p = rhs.p ? new T(*rhs.p) : 0; 
     } 
     deep_copy_ptr<T> & operator=(deep_copy_ptr<T> const & rhs) 
     { 
     if (p != rhs.p) 
     { 
      deep_copy_ptr<T> copy(rhs); 
      swap(copy); 
     } 
     } 
     // ... 
    } 
1

Các std::auto_ptr là một công cụ tốt cho việc quản lý đối tượng năng động trong C++ nhưng để sử dụng nó effectivelly điều quan trọng là unserstand công trình như thế nào auto_ptr. This article giải thích tại sao, khi nào và ở đâu con trỏ thông minh này nên được sử dụng.

Trong trường hợp của bạn, trước hết bạn nên quyết định những gì bạn muốn làm với đối tượng bên trong auto_ptr của bạn. Nó có nên được nhân bản hay chia sẻ không?

Nếu cần sao chép, hãy đảm bảo nó có một hàm tạo bản sao và sau đó tạo một auto_ptr mới chứa một bản sao đối tượng của bạn xem câu trả lời của câu trả lời là Adam Badura.

Nếu cần chia sẻ, bạn nên sử dụng boost::shared_ptr làm Martin Liversage được đề xuất.

0

Lựa chọn đầu tiên của tôi là tránh auto_ptr trong tình huống này hoàn toàn. Nhưng nếu tôi đã được ủng hộ chống lại một bức tường, tôi có thể cố gắng sử dụng các từ khóa mutable trong tuyên bố _c - điều này sẽ cho phép nó được sửa đổi ngay cả từ một tham chiếu const.

-1

Bạn không thể sử dụng tham chiếu const trong một hàm tạo bản sao hoặc toán tử gán có liên quan đến auto_ptr<>. Loại bỏ const. Nói cách khác, sử dụng các khai báo như

foo(foo & rhs); 
foo & operator=(foo & rhs); 

Các biểu mẫu này được đề cập rõ ràng trong tiêu chuẩn, chủ yếu trong phần 12.8. Chúng có thể sử dụng được trong bất kỳ việc triển khai phù hợp tiêu chuẩn nào. Thực tế, các đoạn 5 và 10 của 12.8 nói rằng hàm tạo bản sao được xác định ngầm và toán tử gán (tương ứng) sẽ lấy tham chiếu không const nếu bất kỳ thành viên nào yêu cầu nó.

+0

Chắc chắn bạn có thể sử dụng chúng; bạn chỉ cần ghi đè lên hàm tạo bản sao implcit và toán tử gán. – JohnMcG

+0

Theo tiêu chuẩn, chúng * là * hàm tạo bản sao ngầm và toán tử gán. Tôi đã tham khảo. Nếu bạn biết về việc triển khai nó sẽ không hoạt động, vui lòng cho chúng tôi biết. –

+0

Việc triển khai của bạn sẽ hoạt động và tôi nghĩ rằng tôi đã không chính xác về phần được tạo ngầm. Điều tôi không đồng ý với câu đầu tiên là bạn không thể sử dụng tham chiếu const trong một hàm tạo bản sao với aut_ptr's. Bạn có thể, bạn chỉ cần có họ làm điều gì đó khác với hành vi mặc định. – JohnMcG

1

Nếu tôi có lớp chứa auto_ptr và muốn ngữ nghĩa sâu, tôi thường chỉ làm điều này cho các lớp có toán tử sao chép ảo, tức là sao chép().

Sau đó, bên trong trình tạo bản sao, tôi khởi tạo auto_ptr thành bản sao() của phương thức khác; ví dụ.

class Foo 
{ 
    public: 
     Foo(const Foo& rhs) : m_ptr(rhs.m_ptr->clone()); 
    private: 
     std::auto_ptr<T> m_ptr; 
}; 

clone() thường được thực hiện như sau:

class T 
{ 
    std::auto_ptr<T> clone() const 
    { 
     return std::auto_ptr<T>(new T(*this)); 
    } 
}; 

Chúng tôi đang áp đặt điều kiện là T là clonable, nhưng tình trạng này chủ yếu được áp đặt bởi có một lớp copiable với một thành viên auto_ptr.

0

Với chỉnh sửa, có vẻ như bạn muốn chuyển giao ngữ nghĩa quyền sở hữu.

Trong trường hợp đó, khi đó bạn sẽ muốn có trình tạo bản sao và toán tử gán chấp nhận tham chiếu không phải const cho đối số của chúng và thực hiện khởi tạo/gán tại đó.

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