2013-06-17 9 views
5
#include<iostream> 
using namespace std; 
class X 
{ 
    int i; 

    public: 
    X(int a=0) : i(a) {} 

    friend X operator+ (const X& left,const X&right); 

}; 
X operator+ (const X& left,const X&right) // Method 1 
{ 
    return X(left.i + right.i); 
} 

X operator+ (const X& left,const X&right) // Method 2 
{ 
    X temp(left.i + right.i); 
    return temp; 
} 

int main() 
{ 
    X a(2),b(3),c; 

    c=a+b; 

    c.print(); 
    return 0; 
} 

Trong mã này, Toán tử + bị quá tải thông qua 2 phương pháp khác nhau.Sự khác biệt trong các phương pháp này của quá tải Nhà khai thác trong C++

Câu hỏi của tôi là sự khác biệt giữa các phương pháp này và cần được xem xét thực tế hơn để sử dụng là gì?

+1

Trong C++ 11, 'return {left.i + right.i}' là phương thứC# 3 mà ... cũng làm điều tương tự (sau khi tối ưu hóa tầm thường). – Yakk

Trả lời

6

Tôi không thể thấy trường hợp bất kỳ trình biên dịch nào sẽ tạo mã khác nhau giữa hai phiên bản đó. Điều thứ hai là tiết kiệm hơn một chút, nhưng trình biên dịch được phép tối ưu hóa thêm bản sao bổ sung trong trường hợp đó và tôi không biết về bất kỳ trình biên dịch nào không làm việc đó.

Điều đó nói rằng, đây là tối ưu hóa vi mô: Viết mã rõ ràng nhất, sau đó đưa tôi đến điểm cuối cùng của tôi. Không viết một trong hai toán tử này, nhưng viết phiên bản thành ngữ kết hợp với +=:

X& operator+=(const X&right) { i += right.i; return *this; } 
X operator+(X left, const X& right) { return left += right; } 
+0

+1. @ MichaelSmith: Tôi nghĩ bạn nên chọn câu trả lời này, nó cung cấp cho bạn những gợi ý đúng về cách thiết kế các nhà khai thác của bạn. –

+0

Nếu bạn định tạo mã 'constexpr', hãy viết toán tử' operator + 'và sau đó viết' operator + = 'về điều đó. – CTMacUser

3

Không có sự khác biệt giữa hai phương pháp này và bạn nên sử dụng phương pháp giao tiếp tốt nhất ý định của mình cho bạn.

Đoạn 12,8/31 trên bản sao sự bỏ bớt quy định cụ thể:

Khi tiêu chí nhất định được đáp ứng, một thực hiện được phép bỏ qua việc xây dựng sao chép/di chuyển của một đối tượng lớp , ngay cả khi các nhà xây dựng được lựa chọn cho hoạt động sao chép/di chuyển và/hoặc destructor cho đối tượng có các tác dụng phụ. Trong trường hợp này, việc triển khai xử lý nguồn và đích của thao tác sao chép/di chuyển bị bỏ qua chỉ đơn giản là hai cách khác nhau đề cập đến cùng một đối tượng và việc hủy bỏ đối tượng đó xảy ra vào cuối thời gian khi hai đối tượng đã bị phá hủy mà không cần tối ưu hóa. sự bỏ bớt này hoạt động sao chép/di chuyển, được gọi là bản sao sự bỏ bớt, được cho phép trong các trường hợp sau (mà có thể được kết hợp để loại bỏ nhiều bản sao):

- trong một tuyên bố return trong một hàm với một kiểu lớp trở lại , khi biểu thức là tên của đối tượng tự động không bay hơi (khác với hàm hoặc tham số mệnh đề bắt buộc) với cùng loại cn-unqualified làm kiểu trả về hàm, có thể bỏ qua thao tác sao chép/di chuyển bằng cách xây dựng đối tượng tự động trực tiếp vào giá trị trả về của hàm

- [...]

Như bạn có thể thấy, cả việc tạo ra một tạm thời và một id-biểu đặt tên một đối tượng địa phương với thời hạn lưu trữ tự động đủ điều kiện để sao chép sự bỏ bớt.

Hơn nữa, trình biên dịch sẽ xử lý địa phương temp như số rvalue (đọc: như tạm thời, trong trường hợp này) cho mục đích trả lại từ một hàm. Đoạn 12,8/32 của C++ 11 tiêu chuẩn quy định cụ thể:

Khi các tiêu chuẩn cho sự bỏ bớt một hoạt động sao chép được đáp ứng hoặc sẽ được đáp ứng tiết kiệm cho một thực tế rằng các đối tượng nguồn là một tham số chức năng, và các đối tượng được sao chép được chỉ định bởi một giá trị, độ phân giải quá tải đến chọn hàm tạo cho bản sao được thực hiện lần đầu tiên như thể đối tượng được chỉ định bởi giá trị. [...]

Bởi vì điều này, tôi sẽ đề nghị mạnh mẽ loại bỏ trình độ const từ kiểu trả về:

const X operator + (const X& left, const X&right) 
// ^^^^^ 
// Don't use this! 

Trong C++ 11, điều này sẽ ức chế ngữ nghĩa di chuyển, bởi vì bạn không thể di chuyển từ một đối tượng const, ngay cả khi nó là một giá trị - trong ngắn hạn, hàm khởi động di chuyển của X, cung cấp một tồn tại, sẽ không được chọn và hàm tạo bản sao sẽ được gọi.

+0

không phải là người đầu tiên sử dụng khái niệm "RVO"? đúng nếu tôi đã sai lầm ? –

+0

Rất có thể, anh ta muốn tạo phương thức const, không phải đối tượng trả về. –

+1

@ T.E.D .: Nó không phải là một hàm thành viên, do đó, nó không thể là 'const' ... –

2

Sự khác biệt là Phương pháp 1 sử dụng khái niệm được gọi là "TÍCH L VALUEI GIÁ TRỊ TRẢ LẠI TRẢ LẠI".

Phương pháp 1:

Khi biên dịch thấy rằng bạn không có sử dụng cho đối tượng nó được tạo nào khác ngoài việc trả lại. Trình biên dịch tận dụng điều này và "nó xây dựng đối tượng trực tiếp tại vị trí của nơi mà giá trị này được cho là được trả về". ở đây, chỉ cần một lời gọi hàm dựng thông thường (không cần có hàm tạo bản sao) và không có cuộc gọi hủy bởi vì bạn chưa bao giờ thực sự tạo đối tượng cục bộ. điều này hiệu quả hơn.


Cách 2:

Đầu tiên tạm thời đối tượng có tên tạm thời được tạo ra. sau đó copy constructor sao chép temp vào vị trí của giá trị trả về oustide. và sau đó hàm hủy được gọi cho temp ở cuối phạm vi.

Cuối cùng phương pháp 1 hiệu quả hơn nhưng đây là tính năng phụ thuộc trình biên dịch.

+4

Mỗi trình biên dịch không chậm phát triển, hiện đại sẽ phát ra chính xác cùng một mã cho cả hai phiên bản. Ngoài ra, bạn đang quên đi ngữ nghĩa di chuyển. – Griwes

+0

nhưng trong phương pháp 2, đối tượng tạm thời được tạo ở đó bao gồm cả hàm gọi hàm tạo của nó. –

+0

Có lẽ tôi đang thiếu một cái gì đó, nhưng tôi không thấy lý do tại sao một người tối ưu hóa sẽ đối xử với hai thói quen đó một cách khác nhau. Cả hai đều trả về một đối tượng được tạo ra tại chỗ không được sử dụng cho bất kỳ mục đích nào nhưng trả về một giá trị. –

0

Triển khai thứ hai sẽ làm tối ưu hóa NRV. Stan Lippman nói NRV tối ưu hóa cần một nhà xây dựng bản sao rõ ràng, nhưng ở đây lớp X là đơn giản, đủ vì vậy tôi không nghĩ rằng NRV cần xây dựng bản sao rõ ràng.