2015-10-24 18 views
6

Tôi đã có câu hỏi về việc sử dụng duy nhất-ptr trước đây. Tôi nhận được this answer đề xuất sử dụng các đối tượng di chuyển. Tôi định nghĩa một lớp như sau:Có một điểm để xác định các đối tượng chuyển động chỉ trong C++ 11 không?

class B { 
    const string objName; 
public: 

    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 

    B(const string & name) : 
      objName(name) { 

    } 

    virtual ~B(); 

    const string name() const { return objName;}; 
} 

và tôi gọi B bằng dòng này:

class A { 
A(){} 
void take(B b); 
} 

A a; 
B b("test"); 
cout<<b.name(); 
a.take(std::move(b)); 
cout<<b.name(); 

Câu hỏi của tôi:

  1. Ngay cả khi tôi đã mặc định các nhà xây dựng di chuyển, tôi không thể viết a.take (b) và tôi nhận được lỗi biên dịch. Tôi hiểu rằng constrctor sao chép bị xóa nhưng Có vẻ như sự lựa chọn hợp lý là sử dụng constructor di chuyển khi nó được mặc định mà không cần viết std :: di chuyển như thế này a.take (b)
  2. Kết quả là "test" được in hai lần. Tại sao đối tượng b không bị xóa sau khi gọi di chuyển? Nếu đối tượng b vẫn còn tồn tại và một bản sao của nó đã được gửi đến a.take (di chuyển (b)) thì có nghĩa là chúng ta không có bất kỳ việc sử dụng di chuyển nào cho các đối tượng rvalue.
  3. Có thực hành tốt để sử dụng các đối tượng chỉ di chuyển như trên (Loại bỏ nhà xây dựng bản sao và toán tử gán và hàm tạo di chuyển mặc định và chuyển bài)?
+0

Thử thay thế "thử nghiệm" bằng chuỗi dài hơn và xem điều gì xảy ra. Nói chung giá trị của đối tượng chuyển từ không xác định; nó có thể là giá trị cũ hay không. –

+3

Bạn cần xóa 'const' khỏi' objName' nếu bạn muốn di chuyển từ nó. –

+0

@AlanStokes xóa const sẽ gây ra thử nghiệm đó sẽ được in một lần. Điều đó có nghĩa là các bộ nhớ const sẽ không bao giờ được di chuyển? Hoặc chúng sẽ được sao chép? – Govan

Trả lời

4

Có, có một điểm. Các đối tượng quản lý tài nguyên (có thể là các tài nguyên vật lý) mà không thể/không nên được chia sẻ giữa các đối tượng là ví dụ đầu tiên xuất hiện trong tâm trí.

1) Bạn đã viết không chính xác. Đây là những gì tôi nghĩ rằng bạn muốn dựa trên câu hỏi này và câu hỏi trước của bạn.

class B { 
    string objName; 
public: 
    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 
    B(const std::string & name) : 
      objName(name) {} 
    virtual ~B() {} 
    std::string name() const { return objName;} 
}; 

class A { 
public: 
    std::vector<B> v; 
    void take(B && b) 
    { 
     v.push_back(std::move(b)); 
    } 
}; 

int main() 
{ 
    A a; 
    B b("test"); 

    std::cout << "Before: " << b.name() << std::endl; 
    a.take(std::move(b)); 
    std::cout << "After: " << b.name() << std::endl; 

    std::cout << "A has elements: " << std::endl; 
    for(B &b : a.v) 
     std::cout << " " << b.name() << std::endl; 
} 

2) Bạn đang truy cập một giá trị đã được di chuyển từ, điều này không có ý nghĩa gì! This answer đã làm tốt công việc giải thích, nhưng bên dưới tôi cũng đã bao gồm văn bản từ tham chiếu STL cho std :: move.

http://en.cppreference.com/w/cpp/utility/move

Trừ khi có quy định khác, tất cả các đối tượng thư viện tiêu chuẩn mà có được chuyển từ được đặt trong tình trạng hợp lệ nhưng không xác định. Tức là, chỉ các chức năng không có điều kiện tiên quyết, chẳng hạn như toán tử gán , có thể được sử dụng an toàn trên đối tượng sau khi nó được di chuyển.

3) Tôi đã tìm thấy hai cách sử dụng hợp pháp trong trải nghiệm của mình. Trong cả hai trường hợp, các đối tượng chỉ di chuyển kiểm soát một nguồn tài nguyên vật lý mà nếu được chia sẻ bởi hai đối tượng sẽ phá vỡ mọi thứ.

+2

Lưu ý rằng không cần xóa một cách rõ ràng ctor sao chép và toán tử gán. Cú pháp di chuyển do người dùng xác định sẽ vô hiệu hóa bản sao, xem ví dụ: ví dụ trực tiếp [ở đây] (http://coliru.stacked-crooked.com/a/d20de6c4f219a22e). – vsoftco

+0

@tweej lý do tại sao tôi nên sử dụng objetcts di chuyển chỉ thay vì duy nhất-ptr? – Govan

+0

@Govan: Trên thực tế, đối với một tài nguyên không thể chia sẻ, 'unique_ptr' là một viên gạch xây dựng và đối tượng chỉ di chuyển của bạn quấn nó lên để trình bày giao diện dễ chịu. Nếu nó không thể được chia sẻ, làm thế nào bạn thậm chí sẽ viết một constructor sao chép? –

2

Giới thiệu 3: tất nhiên là có. Tôi có thể suy nghĩ về nhiều ví dụ về các đối tượng có thể được di chuyển và không được sao chép, hoặc dù sao.

Một ví dụ là một lớp Socket:
1) bạn muốn cung cấp hàm khởi tạo mặc định cho Ổ cắm "trống" mà vẫn không nhận được bất kỳ IP hoặc cổng nào.
2) bạn muốn cung cấp cho một nhà xây dựng được một IP và cổng.khi xây dựng Socket sẽ cố gắng để kết nối và có thể ném một ngoại lệ nếu kết nối không
3) điều rõ ràng là destructor - nó sẽ ngắt kết nối các đối tượng và phát hành bất kỳ tài nguyên cơ bản hệ thống mà đối tượng này có thể giữ

gì về di chuyển constructor vs constructor sao chép?
giả sử tôi có chức năng tạo ổ cắm và trả về. Nếu tôi gọi hàm tạo bản sao có nghĩa là:
ổ cắm sao chép mới (giá trị trả về) sẽ cố gắng kết nối với cùng một IP và cổng mà ổ cắm nguồn được kết nối. điều đó hoàn toàn không thể xảy ra được.
ổ cắm nguồn sẽ bị ngắt kết nối và bị phá hủy

có thể là việc sao chép ổ cắm sẽ tạo ra một mớ hỗn độn lớn. ngược lại, một hàm tạo di chuyển giải quyết khối lượng này một cách thận trọng:
giá trị trả lại nhận tất cả các tài nguyên hệ điều hành cơ bản mà không ngắt kết nối chúng hoặc hủy chúng.
ổ cắm nguồn vẫn trống và trình hủy không có gì để ngắt kết nối hoặc phá hủy.

dường như các ổ cắm có nhiều khả năng chỉ được di chuyển, không được sao chép.

điều này có thể áp dụng cho lớp "Quy trình" - Nếu tôi cố gắng trả lại quy trình bằng cách sao chép, tôi có thể thử mở lại quy trình tương tự, chỉ để tắt bản gốc. một mớ hỗn độn! bằng cách di chuyển quy trình xung quanh tôi không làm điều đó. Tôi chỉ đơn giản là di chuyển quá trình xung quanh từ chức năng đến chức năng.

+0

Cảm ơn câu trả lời của bạn. Tôi hiểu sự cần thiết phải di chuyển và khác biệt giữa mover và copy. Những gì tôi đang tự hỏi là cần phải di chuyển cho các đối tượng rvalue. Bạn có thể thực hiện socket bằng unique_ptr. Câu hỏi là sự khác biệt giữa di chuyển (unique_ptr (socket mới())) hoặc di chuyển (Socket()) – Govan

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