8

Xét đoạn mã sau:Sao chép constructor không gọi khi khởi tạo một đối tượng với giá trị trả về của một hàm

#include <iostream> 

using namespace std; 

class A 
{ 
    public: 
     int a; 
     A(): a(5) 
     { 
      cout << "Constructor\n"; 
     } 
     A(const A &b) 
     { 
      a = b.a; 
      cout << "Copy Constructor\n"; 
     } 
     A fun(A a) 
     { 
      return a; 
     } 
}; 

int main() 
{ 
    A a, c; 
    A b = a.fun(c); 
    return 0; 
} 

Kết quả của đoạn mã trên với g++ file.cpp là:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 

Đầu ra của trên mã với g++ -fno-elide-constructors file.cpp là:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 
Copy Constructor 

tôi biết Return Value optimization ation. Câu hỏi của tôi là gọi để sao chép constructor được elided (đối tượng tạm thời trong khi trở về hoặc trả lại đối tượng được sao chép vào b)?

Nếu hàm tạo bản sao ưu tiên là hàm tạo được sử dụng để tạo b, thì b được tạo ra như thế nào (vì không có lệnh gọi hàm tạo nào trong trường hợp này)?

Nếu tôi thay thế dòng A b = a.fun(c); với a.fun(c) và biên dịch bằng cách sử dụng phương pháp đầu tiên hoặc thậm chí các phương pháp thứ hai, sau đó cũng constructor sao chép đang được gọi là 2 lần. Vì vậy, nếu trong trường hợp được giải thích trong đoạn trước, thì hàm tạo bản sao của đối tượng tạm thời được elided, thì tại sao nó không được elided trong trường hợp này?

+0

Làm thế nào tôi kiểm tra công cụ này khi tôi đã học về nó tất cả là 'std :: cout <<" Sao chép constructor: "<< (void *) b <<" to "<< (void *) << std :: endl;' và 'std :: cout <<" Xây dựng "<< (void *) << std :: endl' này. Điểm thưởng nếu bạn thêm di chuyển vào C++ 11. – IdeaHat

Trả lời

6
#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    int a; 
    A(): a(5) 
    { 
     cout << "Constructing: " << (void *)this << std::endl; 
    } 
    A(const A &b) 
    { 
     a = b.a; 
     cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl; 
    } 
    A fun(A a) 
    { 
     return a; 
    } 
}; 

int main() 
{ 

    A a, c; 
    A b = a.fun(c); 

    std::cout << "a:" << (void *)&a << std::endl << 
       "b:" << (void *)&b << std::endl << 
       "c:" << (void *)&c << std::endl; 
    return 0; 
} 

Sản lượng:

Constructing: 0x7fffbb377220 
Constructing: 0x7fffbb377210 
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210 
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230 
a:0x7fffbb377220 
b:0x7fffbb377200 
c:0x7fffbb377210 

Vì vậy, nó xây dựng a, xây dựng c, bản c đến một trung gian (lập luận a của hàm), và sau đó sao chép trung gian trực tiếp vào b, bỏ qua việc sao chép điển hình của từ a đến trả lại Trung gian. Này thậm chí còn tốt hơn đã chứng minh nếu bạn vượt qua bởi giá trị (thay đổi để A fun(const A& a):

Constructing: 0x7fff8e9642b0 
Constructing: 0x7fff8e9642a0 
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0 
a:0x7fff8e9642b0 
b:0x7fff8e964290 
c:0x7fff8e9642a0 

một được xây dựng, c được xây dựng, c được sao chép trực tiếp đến b, mặc dù b không được truyền cho niềm vui

4

Bản sao được chia sẻ là bản sao của giá trị trả lại tạm thời thành b. Không bỏ qua, giá trị trả lại được khởi tạo từ a và được sao chép sang b. Thay vào đó, tạm thời giữ nguyên giá trị trả lại là được xây dựng thành b và được khởi tạo với a. [Class.copy]/31:

khi một đối tượng lớp tạm thời chưa được ràng buộc với một tài liệu tham khảo (12.2) sẽ được sao chép/di chuyển đến một đối tượng lớp với cùng loại cv-không đủ tiêu chuẩn, các hoạt động sao chép/di chuyển có thể được bỏ qua bởi xây dựng các đối tượng tạm thời trực tiếp vào mục tiêu của bản sao bỏ qua /di chuyển

bạn có thể quan sát này nếu bạn thêm một công suất tăng thêm trong fun:

A fun(A a) 
{ 
    cout << "fun!" << endl; 
    return a; 
} 

Sau đó, với sự bỏ bớt bạn sẽ nhận được

[...]
thú vị!
Sao chép Constructor

Và không có:

[...]
thú vị!
Sao chép Constructor
Sao chép Constructor

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