2012-06-08 9 views
13

xem xét:Tôi nên std :: di chuyển một shared_ptr trong một nhà xây dựng di chuyển?

#include <cstdlib> 
#include <memory> 
#include <string> 
#include <vector> 
#include <algorithm> 
#include <iterator> 
using namespace std; 

class Gizmo 
{ 
public: 
    Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {}; 
    Gizmo(Gizmo&& rhs); // Implemented Below 

private: 
    shared_ptr<string> foo_; 
}; 

/* 
// doesn't use std::move 
Gizmo::Gizmo(Gizmo&& rhs) 
: foo_(rhs.foo_) 
{ 
} 
*/ 


// Does use std::move 
Gizmo::Gizmo(Gizmo&& rhs) 
: foo_(std::move(rhs.foo_)) 
{ 
} 

int main() 
{ 
    typedef vector<Gizmo> Gizmos; 
    Gizmos gizmos; 
    generate_n(back_inserter(gizmos), 10000, []() -> Gizmo 
    { 
     Gizmo ret; 
     return ret; 
    }); 

    random_shuffle(gizmos.begin(), gizmos.end()); 

} 

Trong đoạn mã trên, có hai phiên bản của Gizmo::Gizmo(Gizmo&&) - một sử dụng std::move để thực sự di chuyển các shared_ptr, và người kia chỉ sao chép shared_ptr.

Cả hai phiên bản dường như hoạt động trên bề mặt. Một khác biệt (khác biệt duy nhất tôi có thể thấy) là trong phiên bản không move số tham chiếu của shared_ptr tạm thời tăng lên, nhưng chỉ một thời gian ngắn.

Tôi thường tiếp tục và moveshared_ptr, nhưng chỉ để rõ ràng và nhất quán trong mã của tôi. Tôi có thiếu cân nhắc ở đây không? Tôi có nên sử dụng phiên bản này so với phiên bản khác vì bất kỳ lý do kỹ thuật nào kỹ thuật không?

+2

Di chuyển trong một hàm tạo di chuyển ít nhất là phù hợp ngữ nghĩa ... – ildjarn

+1

Tại sao bạn giữ chuỗi trong shared_ptr? Một shared_ptr như là một biến thành viên thường xuyên nhất là một dấu hiệu của thiết kế xấu. –

+1

Di chuyển trong một hàm tạo di chuyển phù hợp với những gì mà trình biên dịch sẽ tự động tạo ra. –

Trả lời

16

Vấn đề chính ở đây không phải là sự khác biệt hiệu suất nhỏ do tăng thêm nguyên tử và giảm trong shared_ptr nhưng ngữ nghĩa của thao tác không nhất quán trừ khi bạn thực hiện di chuyển.

Mặc dù giả định là số tham chiếu của shared_ptr sẽ chỉ là tạm thời nhưng không có sự bảo đảm như vậy trong ngôn ngữ. Đối tượng nguồn mà bạn đang di chuyển có thể là tạm thời, nhưng nó cũng có thể có tuổi thọ dài hơn nhiều. Nó có thể là một biến có tên đã được đúc để một rvalue tham chiếu (nói std::move(var)), trong trường hợp này bằng cách không di chuyển từ shared_ptr bạn vẫn được duy trì chia sẻ quyền sở hữu với nguồn gốc của sự di chuyển, và nếu đích shared_ptr có phạm vi nhỏ hơn thì tuổi thọ của đối tượng nhọn sẽ không cần thiết được mở rộng.

+0

Tôi tự hỏi khi sử dụng di chuyển, mức độ nào chúng ta nên suy nghĩ cho chính mình, "động thái này có thể làm suy giảm một bản sao"? Rõ ràng là chúng ta làm khi viết mã mẫu cho một kiểu 'MoveConstructible' tùy ý' T', vì nó không thực sự có một hàm tạo di chuyển nào cả, hãy để một mình sửa đổi mã nguồn. Rõ ràng, đó là QoI nghèo nếu một đối tượng chuyển từ giữ tài nguyên không cần thiết, vì vậy nếu 'Gizmo' có một hàm khởi động thì nó sẽ là một hàm tốt. Nhưng tôi nghĩ đó là vấn đề về quy ước và cách viết mã mà chúng ta có quyền được như thế nào khi nó không có. –

+0

+1, đây là những gì tôi đã nhận được trong bình luận của tôi về câu trả lời của James. Đặt tốt. – ildjarn

+0

Ví dụ khác, việc thực thi chuyển động hợp lệ là gọi 'swap'. Trạng thái của đối tượng di chuyển từ không xác định, và do đó đặc biệt được phép là trạng thái của đối tượng được chuyển đến. Nhưng bạn sẽ có một chút miffed rằng các nguồn lực của các * di chuyển đến * đối tượng treo xung quanh vô thời hạn. –

11

Việc sử dụng move là thích hợp hơn: nó phải hiệu quả hơn bản sao vì nó không yêu cầu tăng thêm nguyên tử và giảm số lượng tham chiếu.

+0

Có sự khác biệt về ngữ nghĩa: ai có thể mong đợi một sự chuyển từ 'Gizmo' sang trạng thái chia sẻ nội bộ của nó còn sống? Cá nhân tôi sẽ thấy rằng đáng ngạc nhiên, và mong đợi một đối tượng chuyển từ để phát hành tất cả các trạng thái được chia sẻ. – ildjarn

+1

@ildjarn: Tôi đồng ý, mặc dù điều này sẽ không ảnh hưởng đến tính chính xác của chương trình theo cách này: việc di chuyển từ đối tượng sẽ vẫn có thể bị hủy và chuyển nhượng được. –

13

Tôi đã upvoted câu trả lời của James McNellis. Tôi muốn nhận xét về câu trả lời của anh ấy nhưng nhận xét của tôi sẽ không phù hợp với định dạng nhận xét. Vì vậy, tôi đặt nó ở đây.

Một cách thú vị để đo lường tác động hiệu suất của việc di chuyển shared_ptr và sao chép một là sử dụng một cái gì đó như vector<shared_ptr<T>> để di chuyển hoặc sao chép toàn bộ nhóm và thời gian. Hầu hết các trình biên dịch có một cách để bật/tắt ngữ nghĩa di chuyển bằng cách chỉ định chế độ ngôn ngữ (ví dụ: -std = C++ 03 hoặc -std = C++ 11).

Đây là mã tôi chỉ thử nghiệm tại O3:

#include <chrono> 
#include <memory> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    std::vector<std::shared_ptr<int> > v(10000, std::shared_ptr<int>(new int(3))); 
    typedef std::chrono::high_resolution_clock Clock; 
    typedef Clock::time_point time_point; 
    typedef std::chrono::duration<double, std::micro> us; 
    time_point t0 = Clock::now(); 
    v.erase(v.begin()); 
    time_point t1 = Clock::now(); 
    std::cout << us(t1-t0).count() << "\u00B5s\n"; 
} 

Sử dụng kêu vang/libC++ và trong -std = C++ 03 này in ra cho tôi:

195.368µs 

Switching để - std = C++ 11 Tôi nhận được:

16.422µs 

Số dặm của bạn có thể thay đổi.

+2

+1: Chỉ cần quan sát. Khi tôi thích nghi điều này để sử dụng lớp 'Gizmo' của tôi ở trên, các phiên bản' move' và non-'move' gần như giống hệt nhau. Đây là trên MSVC10, sử dụng 'boost :: chrono' thay vì' std :: chrono', được biên dịch x64 Release. –

+0

@JohnDibling: Thú vị. Nếu bạn tìm ra lý do tại sao có sự chênh lệch trong kết quả của chúng tôi, tôi rất muốn nghe về nó. Một điều cần thử: Đặt một ngoại lệ trên hàm khởi tạo của bạn. Tôi không biết liệu MSVC10 có thực hiện điều này hay không. Tôi sẽ rất ngạc nhiên nếu nó đã xem xét làm thế nào không nhận thức muộn. Và tôi thực sự sẽ không mong đợi điều này để tạo sự khác biệt cho vector :: xóa thành viên. Nhưng tuy nhiên, đó là điều đầu tiên tôi sẽ thử. Tôi đang chạy trên Intel Core i5 2,8 GHz (được biên dịch cho 64 bit). Các kết quả của bạn có theo thứ tự vài trăm micro giây hay vài chục micro giây không? –