2013-09-22 74 views
8

Vì vậy, tôi đã luôn luôn được một chút mờ trên C + + con trỏ so với ... bất cứ điều gì khác được gọi là. Giống như,Trả về biến ngăn xếp?

Object* pointer = new Object(); 

vs

Object notpointer(); 

Tôi biết rằng con trỏ có lẽ liên quan đến một giây, nhưng về cơ bản đó là một không-con trỏ. (Có gì nó thực sự được gọi là?)

Hơn nữa, tôi tin rằng đối với một trong những đầu tiên, bạn phải gọi

delete pointer; 

tại một số điểm khi bạn đang thực hiện với nó, phải không? Và cái kia bạn không cần phải lo lắng. Tôi đã đọc rằng đầu tiên được phân bổ trên heap, nhưng thứ hai được phân bổ trên stack, và biến mất khi phương thức trả về.

Tuy nhiên, khi bạn trả lại thứ gì đó (không phải nguyên thủy) từ một hàm?

Một ví dụ điển hình được viết bằng Should I return std::strings?:

std::string linux_settings_provider::get_home_folder() { 
    return std::string(getenv("HOME")); 
} 

Bởi những gì tôi đã viết trước đây, các chuỗi được cấp phát trên stack, và cần được giải phóng khi trở về chức năng, phải không? Tuy nhiên, không ai nói gì về điều đó, vì vậy tôi cho rằng nó hoạt động tốt. Tại sao?

Nói chung, sự khác biệt giữa

return new std::string("pointer"); 

return std::string("not-pointer"); 

là gì?

Ngoài ra, giả sử cả hai công việc, ưu và khuyết điểm của hai là gì?

+1

Thứ hai là hàm. – chris

+0

Bạn đang trộn một số khái niệm ở đây. Một con trỏ chỉ là một biến có thể chứa một địa chỉ bộ nhớ. 'New std :: string' đang thực hiện phân bổ bộ nhớ và xây dựng đối tượng. Hai thứ không cần thiết phải được sử dụng cùng nhau. Trong C++ hiện đại, bạn thường không làm điều này. – greatwolf

+0

Chuỗi được phân bổ trên ngăn xếp. Đó là ** không ** giải phóng; nó bị phá hủy. Nhưng trình biên dịch sao chép nó trước khi phá hủy nó. –

Trả lời

11

Khi bạn quay lại bằng con trỏ, bạn cần trả lại đối tượng được phân bổ động theo cách bạn hiển thị (nghĩa là trả về con trỏ đến đối tượng ngăn xếp dẫn đến hành vi không xác định nếu hành vi này bị hủy sau). Điều này tạo ra một tiềm năng cho rò rỉ bộ nhớ, bởi vì, như bạn đã lưu ý, đối tượng đó cần phải được xóa một cách rõ ràng.

Trở bởi giá trị, mặt khác (tức là đoạn thứ hai) kết quả trong việc sao chép các đối tượng mà bạn trở về từ các đối tượng chồng bạn nên trở thành đối tượng tiếp nhận giá trị trả về:

std::string res = get_home_folder(); // std::string gets copied into res 

Trình biên dịch có thể tối ưu hóa điều này để tránh sao chép thông qua return value optimization.

+0

@BenjaminLindley Bạn nói đúng, điều đó có vẻ không đúng. Cảm ơn! – dasblinkenlight

+0

Cảm ơn! Vì vậy, những gì bạn gọi một biến mà không phải là một con trỏ? – Erhannis

+0

@Erhannis Đúng, 'res' trong đoạn đầu không phải là một con trỏ. – dasblinkenlight

1

Tôi đoán bạn đã không thực sự muốn viết Object notpointer(); vì điều này thực sự tuyên bố một hàm gọi là notpointer trả về một Object. Nếu bạn có nghĩa là Object notpointer; các thực thể được đề cập đang gọi giá trị. Giá trị được, thực sự, được phân bổ trên stack hoặc, khi xảy ra là thành viên của các đối tượng, được nhúng vào đối tượng.

Khi trả lại bất kỳ thứ gì, thực thể được trả lại sẽ được sao chép hoặc chuyển vào vị trí mà giá trị trả lại thực tế được mong đợi. Khi giá trị trả lại được tạo, đối tượng cục bộ, nghĩa là đối tượng trên ngăn xếp, vượt quá phạm vi và bị hủy.Cho rằng các đối tượng cục bộ sẽ biến mất, trình biên dịch được phép tách bản sao và xây dựng đối tượng ngay lập tức ở vị trí chính xác, ngay cả khi trình tạo bản sao và/hoặc trình phá hủy có tác dụng phụ.

Sự khác biệt giữa hai báo cáo lợi nhuận của bạn là

  1. Khi sử dụng new std::string("pointer") bạn nhận được một đối tượng cấp phát trên heap và bạn trả về một con trỏ đến đối tượng. Nó rất dễ dàng để rò rỉ con trỏ đó và bạn muốn được tốt hơn để đặt nó ngay lập tức vào một đối tượng phù hợp, ví dụ, một std::unique_ptr<std::string>:

    return std::unique_ptr<std::string>(new std::string("pointer")); 
    

    Bằng cách này, con trỏ sẽ không được tiết lộ. Tất nhiên, bạn cũng sẽ thay đổi kiểu trả về thành std::unique_ptr<std::string> thay vì std::string*. Lưu ý, phân bổ đống đó thường khá đắt.

  2. Khi sử dụng std::string("value") bạn chỉ cần trả lại biến cục bộ. Có một cơ hội tốt là bản sao sẽ được elided và giá trị trả lại được xây dựng ngay lập tức ở vị trí mà nó nên đi. Không cần phải xem xét bất kỳ tài nguyên nào vì tất cả các đối tượng liên quan sẽ bị hủy tự động. Không có phân bổ heap rõ ràng và phân bổ stack rất nhanh.

Tất nhiên, trong ví dụ cụ thể, std::string thực sự cần phân bổ biểu diễn nội bộ trong cả hai trường hợp. Nếu con trỏ được trả về, nó được đảm bảo rằng không có phân bổ nội bộ bổ sung. Mặt khác, khi trả về giá trị std::string và thực sự cần phải được sao chép, bộ nhớ trong có thể cần phải được cấp phát cho bản sao. Tức là, khi trả về các đối tượng lớn, có thể đòi hỏi nhiều phân bổ bộ nhớ, cách tiếp cận trả về một giá trị sẽ chạy nguy cơ mà nó cần được sao chép. Tuy nhiên, với C++ hiện tại, các đối tượng được di chuyển trong trường hợp xấu nhất, nghĩa là không có nhiều lo ngại về việc sao chép các đối tượng như trong C++ 03.

+0

Xin lỗi; Tôi giả định rằng vì 'std :: string text (" notpointer ");' được gọi là hàm tạo 1 tham số cho 'std :: string',' Object obj(); 'được gọi là hàm tạo 0 tham số cho' Object' . Rõ ràng là không. – Erhannis