2012-11-15 28 views
6

Tôi đọc trong another question rằng khi triển khai một hàm tạo di chuyển, thực hành tốt là std :: di chuyển mỗi thành viên trong danh sách khởi tạo vì nếu thành viên đó là đối tượng khác thì đối tượng di chuyển sẽ được gọi. Giống như vậy ...std :: di chuyển bên trong nhà điều hành chuyển nhượng

//Move constructor 
Car::Car(Car && obj) 
    : 
    prBufferLength(std::move(obj.prBufferLength)), 
    prBuffer(std::move(obj.prBuffer)) 
{ 
    obj.prBuffer = nullptr; 
    obj.prBufferLength = 0; 
} 

Tuy nhiên, trong tất cả các toán tử gán mẫu tôi đã thấy, không có đề cập đến việc sử dụng std :: di chuyển cùng lý do. Nếu thành viên là một đối tượng thì nên std :: di chuyển được sử dụng? Giống như rất ...

//Move assignment 
Car Car::operator=(Car && obj) 
{ 
    delete[] prBuffer; 

    prBufferLength = std::move(obj.prBufferLength); 
    prBuffer = std::move(obj.prBuffer); 

    obj.prBuffer = nullptr; 
    obj.prBufferLength = 0; 
    return *this; 
} 

UPDATE:

Tôi đánh giá cao không có nhu cầu sử dụng std :: di chuyển trong ví dụ tôi đã chọn (kém) tuy nhiên tôi quan tâm đến việc nếu các thành viên là đồ vật.

+0

Tôi chắc chắn cũng quan tâm đến một số làm rõ về điểm này. – goji

+1

'Tôi đọc trong câu hỏi khác', bạn có thể liên kết câu hỏi đó không? Trong ví dụ của bạn không có lý do gì để sử dụng 'std :: move'. –

+0

@JesseGood xem chỉnh sửa, cảm ơn – TomP89

Trả lời

3

Sau khi đọc câu hỏi được liên kết, tôi có thể thấy lời khuyên trong câu trả lời được bỏ phiếu nhiều thứ hai là sử dụng std::move trong danh sách khởi tạo cho hàm khởi động vì không có vấn đề nếu nó là kiểu nguyên thủy hay không, nó sẽ làm điều đúng.Tôi phần nào không đồng ý với điều đó và nghĩ rằng bạn chỉ nên gọi số std::move nếu thích hợp, nhưng đây là sở thích cá nhân của bạn.

Ngoài ra, đối với nhà điều hành chuyển nhượng của bạn, cách bạn có nó vẫn ổn dù tôi nghĩ rằng cuộc gọi không cần thiết std::move nên được xóa riêng. Một lựa chọn khác là sử dụng std::swap sẽ làm điều phù hợp với bạn.

Car Car::operator=(Car && obj) 
{ 
    std::swap(this->prBufferLength, obj.prBufferLength); 
    std::swap(this->prBuffer, obj.prBuffer); 
    return *this; 
} 

Sự khác biệt giữa toán tử gán di chuyển trên và toán tử gán di chuyển của bạn là deallocation bộ nhớ là trì hoãn trong khi phiên bản của bạn deallocates bộ nhớ ngay lập tức, điều này có thể là quan trọng trong một số tình huống.

+1

'swap' sẽ _not_ làm điều đúng ở đây - đối tượng chuyển từ có khả năng rất nặng ở đây, vi phạm nguyên tắc ít ngạc nhiên nhất. – ildjarn

+0

@ildjarn: Điểm thú vị. Tuy nhiên, kể từ khi nó được nói rằng một di chuyển từ đối tượng có một "nhà nước hợp lệ nhưng không xác định", tôi không nghĩ rằng nó vi phạm nguyên tắc ít ngạc nhiên nhất. –

+0

Tôi chắc chắn sẽ tìm thấy nó đáng ngạc nhiên nếu instantiations của các đối tượng container tiêu chuẩn đã không trở thành sản phẩm nào khi di chuyển từ, như là một vấn đề của thực hành tiêu chuẩn. Tức là, không giống như thường, tôi không đưa ra tuyên bố này từ quan điểm của tiêu chuẩn, nhưng từ quan điểm của thực hành được thành lập (meh). – ildjarn

1

Có vẻ như prBuffer là một con trỏ và prBufferLength là một loại loại tích phân, vì vậy move sẽ không tạo ra bất kỳ sự khác biệt nào trong trường hợp cụ thể này vì chúng là cả hai loại cơ bản.

Nếu prBufferstd::string ví dụ, khi đó bạn nên sử dụng move để buộc sử dụng trình dịch chuyển di chuyển hoặc di chuyển toán tử gán.

+0

Có lỗi, tôi nên đề cập đến trong câu hỏi rằng tôi hiện đang sử dụng các loại cơ bản, nhờ làm rõ – TomP89

1

Giống như rất nhiều thứ trong C++ và cuộc sống không có câu trả lời có hoặc không dứt khoát cho câu hỏi.

Điều đó nói rằng, như quy tắc chung nếu thành viên đến sẽ bị xóa/đặt lại/làm trống và gán thành viên đến cho thành viên đích sẽ dẫn đến toán tử gán, bạn sẽ muốn sử dụng lệnh std :: move rằng toán tử gán nhiệm vụ sẽ được gọi thay vì toán tử gán bản sao.

Nếu một toán tử gán sẽ không được gọi (tức là chỉ một bản sao nông sẽ được thực hiện) thì sử dụng std :: di chuyển là không cần thiết.

0

Tôi tin rằng cách tốt nhất để thực hiện chuyển nhượng là sử dụng hàm tạo di chuyển để tạo đối tượng tạm thời mới, rồi hoán đổi nó với đối tượng hiện tại, giống như gán bản sao.

Nó không chỉ tránh trùng lặp mã mà còn ngăn không cho bạn vô tình mắc lỗi, ví dụ: quên di chuyển một thành viên.

+0

Sao chép và hoán đổi là cách để triển khai trình tạo bản sao. Các nhà xây dựng di chuyển chỉ cần trao đổi, không cần tạm thời. –

+0

@MooingDuck: Tuyệt vời, tôi nên nghĩ hai lần trước khi nói điều đó! – Mehrdad

+0

Erm, @MooingDuck, bạn sử dụng bản sao và bất kỳ thứ gì để triển khai bản sao? Bản sao là gì? Tôi nghĩ rằng bạn có nghĩa là sao chép & trao đổi là một cách tốt để thực hiện copy _assignment_ đó là những gì Mehrdad nói ở nơi đầu tiên, cùng với nói về chuyển nhượng di chuyển không phải là một nhà xây dựng di chuyển. –

1

Nếu đối tượng thành viên của bạn có lợi khi di chuyển, bạn chắc chắn nên di chuyển chúng. Một chiến lược khác, được thể hiện trong câu trả lời mà bạn liên kết, đang trao đổi. Trao đổi cũng giống như di chuyển, nhưng nó di chuyển theo cả hai hướng. Nếu lớp của bạn quản lý một tài nguyên, điều này có tác dụng của đối tượng được truyền vào (giá trị rvalue) nhận được dữ liệu không còn muốn. Dữ liệu đó sau đó bị phá hủy bởi destructor của đối tượng đó. Ví dụ, toán tử gán di chuyển của bạn có thể được viết như thế này:

Car Car::operator=(Car && obj) 
{ 
    // don't need this, it will be handled by obj's destructor 
    // delete[] prBuffer; 

    using std::swap; 
    swap(prBuffer, obj.prBuffer); 
    swap(prBufferLength, obj.prBufferLength); 

    return *this; 
} 

Ngoài ra hãy xem tại Copy-and-Swap thành ngữ. Cho phép bạn sử dụng cùng một toán tử gán cho cả việc di chuyển và sao chép, nhưng có một nhược điểm nhỏ là kết quả tự gán trong một bản sao không cần thiết.

+0

Thực ra, trì hoãn việc hủy của đối tượng như bạn đề xuất không phải là một ý tưởng tốt. Nếu 'obj' xảy ra là một lvalue được chuyển rõ ràng, nó sẽ tiếp tục tồn tại sau cuộc gọi. Điều này có thể dẫn đến các vấn đề tinh tế, ví dụ: nếu 'prBuffer' trỏ đến các đối tượng có quyền truy cập độc quyền vào một số tài nguyên. –

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