2013-04-13 25 views
5

Tại sao trong mã ví dụ bên dưới, đối tượng được sao chép hai lần? Theo constructor tài liệu của lớp thread sao chép tất cả các đối số để lưu trữ thread-local để chúng ta có lý do cho bản sao đầu tiên. Điều gì về thứ hai?std :: thread Tại sao đối tượng được sao chép hai lần?

class A { 
public: 
    A() {cout << "[C]" << endl;} 
    ~A() {cout << "[~D]" << endl;} 
    A(A const& src) {cout << "[COPY]" << endl;} 
    A& operator=(A const& src) {cout << "[=}" << endl; return *this;} 

    void operator()() {cout << "#" << endl;} 
}; 

void foo() 
{ 
    A a; 
    thread t{a}; 
    t.join(); 
} 

Output từ trên cao:

[C] 
[COPY] 
[COPY] 
[~D] 
# 
[~D] 
[~D] 

Edit: Vâng vâng, sau khi thêm động thái constructor:

A(A && src) {cout << "[MOVE]" << endl;} 

Đầu ra là như thế này:

[C] 
[COPY] 
[MOVE] 
[~D] 
# 
[~D] 
[~D] 
+2

Chỉ cần sửa một chút: các đối số không được sao chép vào bộ nhớ ** ** cục bộ **, nhưng vào ngăn xếp của chuỗi mới. Thread-local storage là một động vật hoàn toàn khác; biến chủ đề cục bộ, về bản chất, một biến toàn cục cho mỗi luồng, có thể truy cập từ bất kỳ hàm nào trong luồng, với một bản sao riêng biệt trong mỗi luồng. –

+0

Trong thực tế, thứ hai '[COPY]' là '[MOVE]', nhưng bạn không thể nhìn thấy nó bởi vì bạn không có hàm khởi tạo nào được triển khai. – soon

+0

Trong trường hợp trên, [COPY] thứ hai là bản sao. Chỉ khi bạn cung cấp một nhà xây dựng di chuyển hoặc khai báo nó rõ ràng với = mặc định bạn sẽ có được di chuyển. – Klaus

Trả lời

3

Đối với bất kỳ nội dung nào bạn muốn di chuyển hoặc tránh sao chép, hãy ưu tiên di chuyển các nhà xây dựng và std::move.

Nhưng tại sao điều này không tự động xảy ra với tôi?

Di chuyển trong C++ là bảo thủ. Nó thường sẽ chỉ di chuyển nếu bạn viết rõ ràng std::move(). Điều này đã được thực hiện bởi vì di chuyển ngữ nghĩa, nếu mở rộng vượt ra ngoài hoàn cảnh rất rõ ràng, có thể phá vỡ mã cũ hơn. Tự động di chuyển thường bị giới hạn trong các trường hợp rất cẩn thận vì lý do này.

Để tránh các bản sao trong tình huống này, bạn cần phải chuyển a xung quanh bằng cách sử dụng std::move(a) (ngay cả khi chuyển nó vào std::thread). Lý do nó tạo bản sao lần đầu tiên là vì std :: thread không thể đảm bảo rằng giá trị sẽ tồn tại sau khi bạn đã hoàn thành việc xây dựng std :: thread (và bạn chưa chuyển nó một cách rõ ràng). Do đó, nó sẽ làm điều an toàn và tạo một bản sao (không lấy một tham chiếu/con trỏ tới những gì bạn truyền vào và lưu trữ nó: mã không biết bạn có giữ nó sống hay không).

Có cả hàm khởi tạo và sử dụng std::move sẽ cho phép trình biên dịch di chuyển tối đa và hiệu quả cấu trúc của bạn. Nếu bạn đang sử dụng VC++ (với CTP hay không), bạn phải viết một cách rõ ràng hàm khởi tạo, nếu không MSVC sẽ (thậm chí đôi khi sai) khai báo và sử dụng một hàm tạo Sao chép.

0

thử với: chủ đề t {std :: m ove (a)};

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