2014-10-26 14 views
5

Tôi đã viết mã sau đây, nơi tôi cố gắng sao chép giá trị của đối tượng unique_ptr vào một cấu trúc.Sao chép giá trị std :: unique_ptr thông qua dereferencing

#include <iostream> 
#include <memory> 
using namespace std; 

struct S { 
    S(int X = 0, int Y = 0):x(X), y(Y){} 

    // S(const S&) {} 
    // S& operator=(const S&) { return *this; } 

    int x; 
    int y; 
    std::unique_ptr<S> ptr; 
}; 

int main() { 
    S s; 
    s.ptr = std::unique_ptr<S>(new S(1, 4)); 
    S p = *s.ptr; // Copy the pointer's value 
    return 0; 
} 

Nó bật lên lỗi trong Visual C++ 2012:

IntelliSense: không có chuyển đổi người dùng xác định phù hợp với từ "S" thành "S" tồn tại
IntelliSense: không điều hành "=" trận đấu các toán hạng loại toán hạng là: std :: unique_ptr> = std :: unique_ptr>
lỗi C2248: 'std :: unique_ptr < _Ty> :: unique_ptr': không thể truy cập viên tin khai báo trong lớp 'std :: unique_ptr < _Ty> '

Trừ khi tôi bỏ ghi chú các dòng mà tôi đã cố xác định hàm tạo bản sao và toán tử =. Điều này sẽ loại bỏ các lỗi trình biên dịch nhưng không phải lỗi IntelliSense. Nó biên dịch bất kể lỗi IntelliSense hiển thị trong danh sách lỗi.

Vì vậy, tại sao nó không thể sử dụng các hàm mặc định và biên dịch với chúng? Tôi có đang làm bản sao giá trị đúng cách không? Làm thế nào tôi nên xác định các nhà xây dựng bản sao nếu nó cần một?

Trả lời

5

Các constructor sao chép không mặc nhiên tạo ra bởi vì bạn có một người dùng định nghĩa constructor, tại sao là lý do tại sao bạn cố gắng để sao chép một S thất bại.

Nhưng vẫn còn, unique_ptr không copyable, chỉ di chuyển, vì vậy bạn có thể sử dụng một constructor di chuyển cho S:

S(S&& other) : x(other.x), y(other.y), ptr(std::move(other.ptr)) 
{ 

} 

Và gọi nó là:

S p = std::move(s); // Move s to p 

Live demo

+0

Có thực sự không có cách nào để sao chép giá trị của con trỏ ngay cả trên một đối tượng ngăn xếp mà không bị mất nó? Bằng cách đó tôi có nghĩa là để lại thông tin 'ptr' một mình, và chỉ sao chép các giá trị của' x' và 'y'. – cpx

+0

@cpx Toàn bộ điểm của một 'unique_ptr' là nó là duy nhất, vì vậy không có đối tượng nào khác có thể ghi vào bộ nhớ của con trỏ. Nếu bạn muốn nhiều hơn một đối tượng tham chiếu đến bộ nhớ, bạn cần sử dụng lớp thích hợp, có thể là 'shared_ptr'. – IllusiveBrian

+0

Có thể sao chép dữ liệu được chỉ bởi 'unique_ptr' và đặt bản sao đó bên trong một' unique_ptr' mới. Tôi cho rằng nó hoàn toàn hợp lý. Con trỏ ban đầu vẫn là 'duy nhất', vẫn là con trỏ duy nhất đến đối tượng ban đầu đó. Bạn có thể chọn một con trỏ khác với đối tượng * khác * với cùng giá trị. Ví dụ. 'auto x1 = make_unique (7); auto x2 = make_unique (7); '** Nhưng **, nó sẽ dẫn đến việc cắt (và có thể hành vi không xác định) nếu bạn cố gắng sao chép giá trị tại một' unique_ptr 'mà thực sự trỏ đến một' Nguồn gốc '. (Có cách giải quyết cho điều đó nữa.) –

2

std::unique_ptr không phải là bản sao có thể tạo và cũng không thể chuyển nhượng được.

Trình điều khiển gán bản sao ẩn và hàm tạo cho S sẽ bị lỗi và do đó có thông báo lỗi.

Bạn tuy nhiên có thể sử dụng S p = std::move(s); như std::unique_ptr là Move constructible và Move gán,

+0

Câu trả lời này là đúng, nhưng tôi nghĩ rằng thiết lập vòng tròn trong câu hỏi. Vấn đề thực sự là bản thân 'S' không phải là copy-constructible hoặc copy-assignable. Lý do 'S' là vấn đề bởi vì nó có một thành viên' unique_ptr '- thành viên có thể là' unique_ptr 'thay vì' unique_ptr 'và nó vẫn dẫn đến cùng một vấn đề. –

2

Vì vậy, tại sao nó không thể sử dụng các hàm mặc định và biên dịch với chúng?

Theo như tôi hiểu, ý tưởng đằng sau unique_ptr container là rằng nó chỉ xử lý cuộc đời của nội dung của nó (một con trỏ đến T), cho đến khi bị miễn nhiệm đó (sử dụng swap hoặc reset phương pháp), hoặc có phá hủy hiệu quả nội dung của nó (khi nó bị phá hủy). Thuộc tính quan trọng thứ hai của unique_ptr là thuộc tính phải cho phép loại không đầy đủ cho T (để hỗ trợ con trỏ mờ).Điều đó có nghĩa là giá trị được chứa có thể không là CopyConstructible. Bởi vì điều này, unique_ptr chính nó không được phép là CopyConstructible.

Tôi có làm bản sao giá trị đúng cách không? Làm cách nào để xác định hàm tạo bản sao nếu cần?

Nếu T kết thúc lên được CopyConstructible, như bạn muốn làm điều đó, bạn phải xử lý các bản sao bằng tay, bằng cách truy cập con trỏ, như bạn đang làm nó trong main. Các nhà xây dựng bản sao có lẽ nên làm điều tương tự.

2

Không phải là một câu trả lời hoàn chỉnh, chỉ cần cung cấp thông tin:

Tôi rất khuyên bạn nên thêm tầm nhìn vào thử nghiệm của bạn:

std::ostream& 
operator<<(std::ostream& os, const S& s) 
{ 
    os << '{' << s.x << ", " << s.y << ", "; 
    if (s.ptr != nullptr) 
     os << s.ptr.get() << ':' << *s.ptr; 
    else 
     os << "nullptr"; 
    return os << '}'; 
} 

Bây giờ bạn có thể nói những câu như:

cout << "s = " << s << '\n'; 

tại nhiều nơi trong thử nghiệm của bạn và thực sự có được hình ảnh tốt về những gì đang xảy ra sau mỗi bước. Điều này sẽ giúp bạn phân tích và tiếp tục trong thiết kế của bạn.

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