2015-01-05 25 views
10

Giả sử tôi có một lớp quản lý bộ nhớ và do đó cần các hàm thành viên đặc biệt do người dùng định nghĩa (tưởng tượng vector hoặc tương tự).Thực hiện nhiệm vụ di chuyển theo phương thức destructor và di chuyển constructor

Xem xét việc thực hiện sau đây của các nhà điều hành di chuyển-nhượng:

Class& operator=(Class&& rhs) 
{ 
    this->~Class();     // call destructor 
    new (this) Class(std::move(rhs)); // call move constructor in-place 
} 
  1. hợp lệ để thực hiện một hành động-phân công theo cách này? Đó là, không gọi một destructor và constructor theo cách này không chạy afoul của bất kỳ quy tắc đời đối tượng trong ngôn ngữ?

  2. Đây có phải là ý tưởng hay để triển khai toán tử chuyển bài theo cách này? Nếu không, tại sao không, và là có một cách kinh điển tốt hơn?

+3

Điều gì sẽ xảy ra nếu lớp '~ Class' hoặc' mới (this) Class (std :: move (rhs)) 'ném? – Yakk

Trả lời

6

Nó không hợp lệ: Điều gì sẽ xảy ra nếu chuyển động này được gọi là một phần của việc di chuyển đối tượng con? Sau đó, bạn tiêu diệt các con (giả sử nó có một destructor ảo) và tái tạo ở vị trí của nó một đối tượng cha mẹ.

Tôi có thể nói rằng ngay cả trong bối cảnh không ảo, nó vẫn là một ý tưởng tồi bởi vì bạn không thấy cú pháp rất thường xuyên và nó có thể làm cho mã khó khăn hơn để grok cho người bảo trì trong tương lai.

Cách tiếp cận tốt nhất là tránh phải viết toàn bộ hàm khởi tạo của riêng bạn (và sử dụng mặc định) bằng cách yêu cầu tất cả các thành viên trong lớp của bạn tự lo việc di chuyển. Ví dụ dựa trên unique_ptr, vv Không có vẻ như thực hiện nó trong điều khoản của trao đổi (như sao chép và trao đổi cho việc chuyển nhượng bản sao) sẽ là một cơ chế dễ hiểu.

2
  1. Nó có thể hợp lệ (1). Để giải quyết vấn đề cụ thể của bạn về thời gian sống của dtor/ctor, có nghĩa là hợp lệ (2). Đó là cách thực hiện ban đầu cho vectơ hoạt động.
  2. Nó có thể là một ý tưởng tốt (nó có lẽ không phải là), nhưng bạn có thể không muốn có một cách kinh điển. (3)

(1) Có tranh cãi về việc có hay di chuyển cần phải có giá trị trong trường hợp tự di chuyển. Tranh luận về an toàn tự di chuyển là vị trí mã nên an toàn (duh), chúng tôi chắc chắn mong đợi việc chuyển nhượng tự được an toàn. Ngoài ra một số báo cáo trải nghiệm người dùng cho nhiều thuật toán sử dụng di chuyển, tự di chuyển là có thể và tẻ nhạt để kiểm tra.

Tranh luận về an toàn tự di chuyển là vị trí mà toàn bộ điểm di chuyển ngữ nghĩa là tiết kiệm thời gian và vì lý do đó di chuyển nên càng nhanh càng tốt. Một kiểm tra tự di chuyển có thể tốn kém so với chi phí di chuyển. Lưu ý rằng trình biên dịch sẽ không bao giờ tạo mã tự di chuyển, bởi vì các giá trị tự nhiên (không được đúc) không thể tự di chuyển được. Cách duy nhất để có một tự di chuyển là nếu một "std :: move()" cast được gọi một cách rõ ràng. Điều này đặt gánh nặng cho người gọi của std :: move() để xác minh rằng tự di chuyển không liên quan hoặc thuyết phục bản thân rằng nó không phải là. Lưu ý rằng nó sẽ là tầm thường để tạo ra một người dùng xác định tương đương với "std :: di chuyển" đã kiểm tra để tự di chuyển và sau đó không làm gì cả. Nếu bạn không hỗ trợ tự di chuyển, bạn có thể muốn ghi lại điều đó.

(2) Đây không phải là mẫu để bạn có thể biết cụm từ "new (this) Class (std :: move (rhs));" có thể ném. Nếu có thể, thì không, điều này không hợp lệ.

(3) Mã này có thể là một câu đố cho người bảo trì, những người có thể mong đợi một cách tiếp cận trao đổi truyền thống hơn, nhưng có một nhược điểm tiềm năng đối với phương pháp hoán đổi. Nếu các tài nguyên được phát hành bởi mục tiêu cần được phát hành càng sớm càng tốt (chẳng hạn như một mutex), thì swap có nhược điểm là các tài nguyên được hoán đổi vào đối tượng nguồn di chuyển. Nếu di chuyển là kết quả của cuộc gọi đến "std :: move()", thì đối tượng nguồn di chuyển có thể không được xử lý ngay lập tức. (Vì đây không phải là mẫu bạn có thể biết tài nguyên nào đang được giải phóng. Nếu bộ nhớ là tài nguyên duy nhất được giải phóng, thì đây không phải là vấn đề.)

Cách tiếp cận tốt hơn có thể là để giải phóng tài nguyên mã từ hàm hủy và mã chuyển tài nguyên từ hàm khởi tạo di chuyển và sau đó chỉ cần gọi các toán tử (nội tuyến) trong toán tử gán di chuyển này.

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