2012-02-12 42 views
5

Các mã sau đây gọi destructor 4 lần:Return Value Tối ưu hóa - C++ - Destructor gọi

#include<iostream> 
using namespace std; 

class A{ 
    public: 
    A(){cout<<"A"<<endl;} 
    ~A(){cout<<"~A"<<endl;} 
    A f(){cout<<"F"<<endl; A b; return b;} 
}; 

int main(){ 
    A a,b; 
    b=a.f(); 
} 

OUTPUT:

A 
A 
F 
A 
~A 
~A 
~A 
~A 

thể một số một xin giải thích? Tôi đã nghĩ rằng chỉ nên có ba cuộc gọi hủy.

+0

Không, không. http://ideone.com/ywGdo –

+0

Rất tiếc! Tôi đã sử dụng Codepad .. http://codepad.org/1OJGoYGP – Venky

+0

Đừng gắn thẻ các câu hỏi của bạn với "c" khi chúng nói về C++. Các ngôn ngữ không giống nhau. – tinman

Trả lời

1

Trình biên dịch của bạn không tối ưu hóa nó. Bạn đã biên dịch nó với tối ưu hóa được bật chưa?

Dưới đây là sản phẩm của cùng mã, biên dịch với gcc:

A 
A 
F 
A 
~A 
~A 
~A 
8

Có hai đối tượng trong main(), vì vậy các destructor sẽ được gọi hai lần chỉ vì trong số họ. Một đối tượng trong f(), do đó, destructor sẽ được gọi là một thời gian chỉ vì nó. Tổng cộng 3 lần (mà bạn mong đợi, nhưng đọc trên ...)

Bây giờ, lần thứ tư destructor được gọi là đối tượng tạm thời được tạo ra khi trở về từ f. Điều này có thể xảy ra chỉ khi không có RVO. RVO là sự lựa chọn của trình biên dịch có nghĩa là nó có thể tối ưu hóa nó, hoặc nó có thể không. Ngôn ngữ không cung cấp bất kỳ sự bảo đảm nào về RVO.

Dù sao, chỉ cần tăng cấp độ tối ưu hóa của bạn; Tôi chắc rằng bạn sẽ chỉ thấy tối đa 3 lời gọi phá hoại.

0

Bạn không thể dựa vào RVO để xảy ra. Đó là lý do tại sao bạn không bao giờ nên đặt logic chức năng bên trong destructors hoặc sao chép các nhà xây dựng (có, những người quá có thể được elided).

Tối ưu hóa giá trị trả lại chỉ là điều mà tiêu chuẩn cho phép, nhưng không thực thi.

Không tối ưu hóa hoặc O2, tôi cũng nhận được 4 cuộc gọi phá hoại.

Với đầy đủ tối ưu hóa - Sửu - Tôi chỉ nhận được 3.

+0

/O2 cũng chứa NRVO, có nghĩa là chỉ có 3 destructor được gọi. Kiểm tra http://msdn.microsoft.com/en-us/library/8f8h5cxt.aspx. – LihO

+0

@LihO Tôi đã kiểm tra mã. Vì vậy, tôi đoán có một lỗi trong MSVS, nếu những gì bạn nói là chính xác. –

+0

Tôi cũng đã thử nghiệm nó (MSVS2010). Với Tối ưu hóa bị vô hiệu hóa ('/ Od') 4 destructors được gọi. Với '/ O1' hoặc'/O2', 3 hàm hủy được gọi. – LihO

1

Có một sự sáng tạo ẩn và sự hủy diệt của một thể hiện của A: khi bạn trở về từ chức năng f(), một bản sao tạm thời của đối tượng b được tạo ra. Nó được gán cho b trong main() và sau đó bị hủy.

+0

Anh ấy hỏi tại sao RVO không xảy ra. –

+0

@Luchian Bạn nói đúng nhưng vì anh ấy không đề cập đến mức tối ưu được sử dụng (hay không), tôi đã bị ấn tượng vì anh ấy chỉ bỏ qua điều đó tạm thời khi trở về theo giá trị. –

0

Biến cục bộ trong f được sao chép thành biến tạm thời khi hàm trả về. Đó là lý do tại sao có bốn cuộc gọi hủy. (Thao tác sao chép gọi là bản sao constructor A(A&) không constructor mặc định của bạn A(), vì thế ba A s.)

+0

Đó không phải là câu hỏi. Anh ấy hỏi tại sao tối ưu hóa giá trị trả lại không áp dụng. –

+0

Tôi không chắc anh ta/cô ấy thậm chí còn nhận thức được RVO hay không. Câu hỏi đơn giản là "Tại sao có bốn cuộc gọi hủy thay vì ba cuộc gọi?" Tôi trả lời. –

+0

Bạn đã đọc tiêu đề chưa? –

3

Có 2 đối tượng trong chính: A a,b;, một đối tượng trong cơ thể của chức năng f(): A b; và sau đó là đối tượng tạm thời đang được sao chép và bản sao của nó được lưu trữ vào b.

Khi trả về b trong phần chức năng của bạn, bản sao được tạo lúc đầu, sau đó địa chỉ b bị hủy, sau đó sao chép được gán vào biến b được khai báo chính và sau đó bản sao này bị hủy.

Add sau dòng đến lớp A nét và nhìn thấy mình:

A(const A&) { cout << "copying" << endl; } 

Với Named Return Value Optimization, trình biên dịch cố gắng để loại bỏ dư thừa constructor Sao chép và Destructor gọi có nghĩa là địa phương b từ chức năng f() sẽ được chỉ định thành biến số b trong chính mà không cần sao chép. Vì vậy, với RVO/NRVO chỉ có 3 đối tượng được tạo trong trường hợp của bạn.

Mặc dù có một cách làm thế nào để tránh huỷ bản sao này mà không RVO trong trường hợp của bạn:

A a; 
A b = a.f(); 

trong trường hợp này bản sao của giá trị trả về của hàm f() được tạo ra và lưu trữ như là một biến b. Điều này cũng có nghĩa là không có nhà điều hành phụ trách nào được gọi và chỉ có 2 đối tượng được tạo trong chính: a và bản sao của b được trả về bởi f().

Hy vọng điều này sẽ hữu ích.