Là sao chép và trao đổi thành ngữ vẫn còn hữu ích trong C ++ 11


0 Phiếu
Đã hỏi 27/4/2016 bởi Veras (1,080 điểm)
Tôi tham khảo các câu hỏi này: thành ngữ bản sao và trao đổi là gì? có hiệu quả, câu trả lời ở trên dẫn đến việc thực hiện:
class MyClass
{
public:
    friend void swap(MyClass & lhs, MyClass & rhs) noexcept;
    MyClass() { /* to implement */ };
    virtual ~MyClass() { /* to implement */ };
    MyClass(const MyClass & rhs) { /* to implement */ }
    MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
    MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};
void swap( MyClass & lhs, MyClass & rhs )
{
    using std::swap;
    /* to implement */
    //swap(rhs.x, lhs.x);
}
Tuy nhiên, nhận thấy rằng chúng tôi có thể eschew việc swap() hoàn toàn, làm như sau:
class MyClass
{
public:
    MyClass() { /* to implement */ };
    virtual ~MyClass() { /* to implement */ };
    MyClass(const MyClass & rhs) { /* to implement */ }
    MyClass(MyClass && rhs) : MyClass() { *this = std::forward<MyClass>(rhs);   }
    MyClass & operator=(MyClass rhs)
    { 
        /* put swap code here */ 
        using std::swap;
        /* to implement */
        //swap(rhs.x, lhs.x);
        // :::
        return *this;
    }
};
lưu ý rằng điều này có nghĩa rằng chúng tôi sẽ không còn có một đối số hợp lệ phụ thuộc vào tra cứu trên std::swap với MyClass. Trong ngắn hạn, là có bất kỳ lợi thế của việc có phương pháp swap().
chỉnh sửa: tôi nhận ra đó là một sai lầm khủng khiếp trong việc thực hiện lần thứ hai ở trên, và nó khá một điều lớn vì vậy tôi sẽ rời khỏi nó như là-là để hướng dẫn bất cứ ai đã đi qua đây. Nếu operator = được xác định là
MyClass2 & operator=(MyClass2 rhs)
sau đó bất cứ khi nào rhs là một r-value, constructor di chuyển sẽ được gọi là. Tuy nhiên, điều này có nghĩa là khi sử dụng:
MyClass2(MyClass2 && rhs)
{
    //*this = std::move(rhs);
}
thông báo bạn đã kết thúc với một cuộc gọi đệ quy đến các nhà xây dựng di chuyển, như nhà điều hành = kêu gọi các nhà xây dựng di chuyển... Điều này là rất tinh tế và khó phát hiện cho đến khi bạn nhận được một thời gian chạy stack tràn. Bây giờ sửa chữa đó sẽ có cả hai
MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)
này cho phép chúng tôi để xác định nhà thầu bản sao như
MyClass2(const MyClass2 & rhs)
{
    operator=( rhs );
}
MyClass2(MyClass2 && rhs)
{
    operator=( std::move(rhs) );
}
thông báo rằng bạn viết cùng một lượng mã, nhà thầu bản sao đến "-miễn phí" và bạn chỉ cần viết nhà điều hành = (&amp;) thay vì bản sao constructor và nhà điều hành =(&amp;&amp;) thay vì phương pháp swap().

2 Câu trả lời

0 Phiếu
Đã trả lời 14/5/2016 bởi vg2590 (250 điểm)
You're certainly not considering the whole picture. Your code re-uses constructors for different assignment operators, the original re-uses assignment operators for different constructors. This is basically the same thing, all you've done is shift it around. Except that since they write constructors, they can deal with non-default-constructible types or types whose values are bad if not initialized explicitly like int or are plain expensive to default-construct or where the default-constructed members are not valid to destruct (for example, consider a smart pointer- an uninitialized T* leads to a bad delete). So basically, all you've achieved is the same principle but in a decidedly worse place. Oh, and you had to define all four functions, else mutual recursion, whereas the original copy-and-swap only defined three functions.
0 Phiếu
Đã trả lời 14/5/2016 bởi better (1,220 điểm)
Trước hết, bạn đang làm nó sai anyway. Thành ngữ bản sao và trao đổi là có để tái sử dụng constructor cho operator chuyển nhượng (và không phải là cách khác xung quanh), lợi nhuận từ xây dựng constructor code đã đúng cách và đảm bảo sự an toàn exception mạnh mẽ cho các nhà điều hành chuyển nhượng. Nhưng bạn đừng gọi trao đổi các nhà xây dựng di chuyển. Trong cùng một cách constructor sao chép sao chép tất cả data (bất cứ điều gì mà có nghĩa là trong bối cảnh nhất định của một lớp học cá nhân), constructor di chuyển di chuyển dữ liệu này, di chuyển constructor xây dựng và gán/giao dịch hoán đổi:
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
và điều này trong phiên bản thay thế của bạn chỉ sẽ
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { using std::swap; swap(x, rhs.x); return *this; }
mà không triển lãm error nghiêm trọng được giới thiệu bằng cách gọi operator chuyển nhượng bên trong các nhà xây dựng. Bạn không bao giờ nên gọi chuyển nhượng operator hoặc trao đổi object toàn bộ bên trong một nhà xây dựng. Nhà thầu đang có để chăm sóc cho việc xây dựng và có lợi thế của việc không có để chăm sóc cho, tốt, phá hủy dữ liệu trước, kể từ khi data đó không tồn tại. Và tương tự như vậy có thể nhà thầu xử lý loại không mặc định nối và cuối cùng nhưng không xây dựng ít nhất là thường trực tiếp có thể là nhiều hơn performant hơn xây dựng mặc theo sau chuyển nhượng/trao đổi. Nhưng để trả lời câu hỏi của bạn, toàn bộ điều này vẫn là thành ngữ bản sao và trao đổi, chỉ cần không có một chức năng rõ ràng swap. Và trong C ++ 11 thậm chí là hữu dụng hơn vì bây giờ bạn đã thực hiện bản sao di chuyển giao với một chức năng duy nhất. Nếu function trao đổi là vẫn còn của value bên ngoài operator nhiệm vụ là một câu hỏi hoàn toàn khác nhau và phụ thuộc nếu type này có thể được đổi chỗ, anyway. Trong thực tế trong C ++ 11 loại di chuyển đúng ngữ nghĩa có thể chỉ được đổi chỗ đầy đủ hiệu quả bằng cách sử dụng thực hiện std::swap mặc định, thường xuyên loại bỏ sự cần thiết cho một trao đổi tùy chỉnh bổ sung. Chỉ cần chắc chắn không phải để gọi này mặc định std::swap bên trong của nhà điều hành nhiệm vụ của bạn, kể từ khi nó thực hiện một di chuyển giao chính nó (trong đó sẽ dẫn đến các vấn đề tương tự như của bạn thực hiện sai lầm của các nhà xây dựng di chuyển). Nhưng để nói nó một lần nữa, tùy chỉnh swap function hay không, điều này không thay đổi bất cứ điều gì ở tính hữu dụng của thành ngữ bản sao và trao đổi, thậm chí còn hữu ích trong C ++ 11, loại bỏ sự cần thiết để thực hiện một chức năng bổ sung.

ToughDev Q&A là gì?

Trang web hỏi đáp cho các bạn đam mê lập trình, phát triển phần mềm và các vấn đề kỹ thuật khác. Với sự giúp đỡ của bạn, chúng tôi hy vọng sẽ xây dựng thành công một thư viện đầy đủ các câu hỏi và trả lời về tất cả các vấn đề có liên quan đến lập trình!







...