2009-07-05 38 views
5

Trong ví dụ này, tại sao việc trả về biến ngăn xếp là ok? Khi t() trả về, tại sao nó không trả về rác, vì con trỏ ngăn xếp đã được tăng lên?trả về biến ngăn xếp C++

#include <<string>> 
#include <<vector>> 
#include <<iostream>> 

using namespace std; 

class X{ 
public: 

    X() { cout << "constructor" << endl; } 

    ~X() { cout << "destructor" << endl; } 

}; 

vector <X> t() 
{ 
    cout << "t() start" << endl; 

    vector<X> my_x; 

    int i = 0; 

    printf("t: %x %x %x\n", t, &my_x, &i); 

    my\_x.push\_back(X()); my\_x.push\_back(X()); my\_x.push\_back(X()); 

    cout << "t() done" << endl; 

    return my_x; 
} 

int main() 
{ 

    cout << "main start" << endl; 

    vector <X> g = t(); 


    printf("main: %x\n", &g); 

    return 0; 

} 

đầu ra:

./a.out 
main start 
t() start 
t: 8048984 bfeb66d0 bfeb667c 
constructor 
destructor 
constructor 
destructor 
destructor 
constructor 
destructor 
destructor 
destructor 
t() done 
main: bfeb66d0 
destructor 
destructor 
destructor 
+2

Đó là nguy hiểm để trả về một tài liệu tham khảo ** ** cho một biến trên stack như bạn có thể kết thúc với rác. Nếu không, một bản sao sẽ được xây dựng và giao cho người gọi. –

Trả lời

5

Bởi vì các thông số được truyền theo giá trị. Một bản sao được thực hiện. Vì vậy, những gì đang được trả lại không phải là giá trị trên ngăn xếp, mà là một bản sao của nó.

+0

Trên thực tế, một bản sao không được thực hiện nhờ vào RVO có tên, như smink nói và như chứng minh truy tìm (địa chỉ của my_x trong hàm t bằng với địa chỉ của g trong hàm main). Nhưng có, kết quả là nếu không một bản sao đã được thực hiện. –

+0

Đúng, nhưng thậm chí không có tối ưu hóa đó, mã của anh ta sẽ hợp lệ, vì giá trị được sao chép, vì vậy những gì được trả về không phải là tham chiếu đến đối tượng không còn giá trị được khai báo trong hàm. – jalf

19

Về cơ bản, khi bạn trả về biến ngăn xếp my_x, bạn sẽ gọi số sẽ gọi hàm tạo bản sao để tạo bản sao mới của biến. Điều này là không đúng, trong trường hợp này, nhờ vào tất cả các trình biên dịch hùng mạnh.

Trình biên dịch sử dụng một mẹo được gọi là trả về theo giá trị tối ưu hóa bằng cách biến biến my_x thực sự được xây dựng ở vị trí bộ nhớ được gán cho g theo phương pháp main. Đây là lý do tại sao bạn thấy cùng một địa chỉ bfeb66d0 đang được in. Điều này tránh phân bổ bộ nhớ và xây dựng bản sao.

Đôi khi điều này không thể xảy ra do sự phức tạp của mã và sau đó trình biên dịch đặt lại về hành vi mặc định, tạo bản sao của đối tượng.

+0

RTO là một chi tiết thực hiện mặc dù. Nhưng tốt nhất. – nos

0

Một bản sao được trả lại, cấu trúc duy nhất bạn không thể trả về một bản sao của nó là một mảng tĩnh. Vì vậy, bạn không thể nói điều này ...

int[] retArray() 
{ 
    int arr[101]; 

    return arr; 
} 
0

Trình biên dịch được tối ưu hóa để xử lý trả về "biến ngăn xếp" mà không cần gọi hàm sao chép. Điều duy nhất bạn cần biết là việc cấp phát bộ nhớ đó nằm trong phạm vi của cả hàm được cấp phát trên ngăn xếp và hàm mà đối tượng được trả về.

Nó KHÔNG gọi hàm tạo bản sao cũng như không phân bổ nó hai lần.

Có thể có một số trường hợp đặc biệt và dĩ nhiên nó phụ thuộc vào trình biên dịch - ví dụ, nguyên thủy có thể được sao chép - nhưng nói chung, các đối tượng được cấp phát trên ngăn xếp sẽ không được sao chép khi được trả về.

Ví dụ:

struct Test { 

}; 

Test getTest() { 
    Test t; 
    std::cout << &t << std::endl;  // 0xbfeea75f 
    return t; 
} 


int main(int argc, char *argv[]) 
{ 
    Test t = getTest(); 
    std::cout << &t << std::endl;  // also 0xbfeea75f 
} 
Các vấn đề liên quan