2010-04-11 72 views
19

Tôi thực sự bối rối về cách thức và phương thức nào để sử dụng để trả về đối tượng từ một hàm. Tôi muốn một số phản hồi về các giải pháp cho các yêu cầu nhất định.Trả về đối tượng từ hàm

Tình huống A: Đối tượng được trả về sẽ được lưu trữ trong một biến không cần phải sửa đổi trong suốt thời gian tồn tại của nó. Như vậy,

const Foo SomeClass::GetFoo() { 
return Foo(); 
} 

gọi như:

someMethod() { 
const Foo& l_Foo = someClassPInstance->GetFoo(); 
//... 
} 

Scneraio B: Các đối tượng quay trở lại là được lưu trữ trong một biến mà sẽ được sửa đổi trong suốt cuộc đời của nó. Như vậy,

void SomeClass::GetFoo(Foo& a_Foo_ref) { 
    a_Foo_ref = Foo(); 
    } 

gọi như:

someMethod() { 
Foo l_Foo; 
someClassPInstance->GetFoo(l_Foo); 
//... 
} 

Tôi có một câu hỏi ở đây: Cho phép nói rằng Foo không thể có một constructor mặc định. Sau đó, làm thế nào bạn sẽ đối phó với điều đó trong tình huống này, vì chúng ta không thể viết này nữa:

Foo l_Foo 

Kịch bản C:

Foo SomeClass::GetFoo() { 
return Foo(); 
} 

gọi như:

someMethod() { 
Foo l_Foo = someClassPInstance->GetFoo(); 
//... 
} 

Tôi nghĩ đây không phải là cách tiếp cận được khuyến nghị vì nó sẽ phải xây dựng thêm thời gian.

Bạn nghĩ sao? Ngoài ra, bạn có đề xuất một cách tốt hơn để xử lý thay thế này không?

+2

http://en.wikipedia.org/wiki/Return_value_optimization – vladr

+0

kịch bản A là tốt nhưng thực sự không cải thiện so với C.Tôi muốn sử dụng C trừ khi hàm tạo mặc định của Foo là "nhanh", ctor sao chép của Foo "chậm" và bạn không muốn dựa vào trình biên dịch đủ thông minh để bỏ các bản sao không cần thiết trong trường hợp B cũng được chấp nhận. – sellibitze

+0

lưu ý rằng 'a_Foo_ref = Foo();' tạo đối tượng và thực hiện sao chép. Nó gần như tương đương với kịch bản cuối cùng – Anycorn

Trả lời

16

Đầu tiên, chúng ta hãy nhìn vào những điều mà đi vào chơi ở đây:

(a) Mở rộng vòng đời của một tạm thời khi nó được sử dụng để khởi tạo một tài liệu tham khảo - Tôi đã học được về nó trong this publication bởi Andrei Anexandrescu. Một lần nữa, nó cảm thấy kỳ lạ nhưng hữu ích:

class Foo { ... } 

Foo GetFoo() { return Foo(); } // returning temporary 

void UseGetFoo() 
{ 
    Foo const & foo = GetFoo(); 
    // ... rock'n'roll ... 
    foo.StillHere(); 
} 

Quy tắc nói rằng khi tham chiếu được khởi tạo với tạm thời, thời gian tạm thời được kéo dài cho đến khi tham chiếu nằm ngoài phạm vi. (this reply trích dẫn kinh điển)

(b) Giá trị Return Tối ưu hóa - (wikipedia) - hai bản sao địa phương -> giá trị trả về -> địa phương thể được bỏ qua trong những trường hợp. Đó là một quy tắc đáng ngạc nhiên, vì nó cho phép trình biên dịch thay đổi hành vi quan sát, nhưng hữu ích.

Có bạn có nó. C++ - kỳ lạ nhưng hữu ích.


Vì vậy, nhìn vào kịch bản của bạn

Kịch bản A: bạn đang trả lại một tạm thời, và gắn nó vào một tài liệu tham khảo - cuộc đời của tạm thời được mở rộng đến tuổi thọ của l_Foo.

Lưu ý rằng điều này sẽ không hoạt động nếu GetFoo sẽ trả về tham chiếu thay vì tạm thời.

Kịch bản B: trình, ngoại trừ việc nó lực lượng một Construct-Xây dựng-Copy-Cycle (có thể đắt hơn nhiều so cấu trúc duy nhất), và các vấn đề bạn đề cập về đòi hỏi một constructor mặc định.

Tôi sẽ không sử dụng mẫu đó để tạo đối tượng - chỉ để thay đổi đối tượng hiện có.

Kịch bản C: Bản sao của thời gian có thể được trình biên dịch bỏ qua (theo quy tắc RVO). Thật không may là không có bảo đảm - nhưng trình biên dịch hiện đại thực hiện RVO.

Rvalue references trong C++ 0x cho phép Foo triển khai trình tạo hàm tạo thông tin tài nguyên không chỉ đảm bảo áp đảo các bản sao, mà còn có ích trong các trường hợp khác.

(tôi nghi ngờ rằng có một trình biên dịch mà thực hiện tài liệu tham khảo rvalue nhưng không RVO. Tuy nhiên có một kịch bản mà RVO không thể đá trong.)


Một câu hỏi như thế này đòi hỏi phải nhắc đến con trỏ thông minh, chẳng hạn như shared_ptrunique_ptr (sau này là "an toàn" auto_ptr). Họ cũng đang ở C++ 0x. Chúng cung cấp một mẫu thay thế cho các hàm tạo các đối tượng.


+0

@peter: Cảm ơn bạn đã trả lời chi tiết như vậy với các liên kết đó! Tôi rất trân trọng điều này. Tôi đã cố gắng tìm ra điều này trong một thời gian, vì vậy tôi quen với kịch bản A. Tôi có 1 câu hỏi về kịch bản B. Tôi thường sử dụng B khi tôi phải sửa đổi một đối tượng chứ không phải khi tôi phải tạo nó bên trong GetFoo (). Bây giờ, giả định rằng; khi chúng ta gọi 'Foo l_Foo; someClassPInstance-> GetFoo (l_Foo); ':: Chúng tôi gửi tham chiếu của l_Foo, do đó không có thời gian ở đây. Và sau đó, trong 'void SomeClass :: GetFoo (Foo & a_Foo_ref) {// ..}' cho phép chỉ nói, tôi sửa đổi nó và không tạo ra nó. Bạn có nghĩ rằng B là một lựa chọn tốt – brainydexter

+0

Ngoài ra, hãy xem nhận xét cuối cùng của tôi cho câu hỏi ban đầu. – brainydexter

+0

@brainydexter: Vâng, đó là một kịch bản sử dụng điển hình để tham khảo. Nó đòi hỏi một số tài liệu (là nó ra hoặc vào/ra vv). Cảm ơn bạn đã tham khảo, tôi sẽ cập nhật bằng một liên kết. --- Gần đây tôi đã đùa giỡn với các tài liệu tham khảo rvalue - đó cũng là một bài tập tốt cho tôi. – peterchen

4

Trong ba trường hợp, số 3 là phương pháp ideomatic và phương pháp bạn nên sử dụng. Bạn sẽ không phải trả tiền cho các bản sao thêm vì trình biên dịch được tự do sử dụng copy elision để tránh bản sao nếu có thể.

Secnario A sai. Bạn kết thúc với một tham chiếu đến một tạm thời bị phá hủy khi câu lệnh chứa hàm call kết thúc. Okay, Kịch bản A là không sai, nhưng bạn vẫn nên sử dụng Kịch bản C.

Secnario B làm việc tốt, nhưng:

phép nói rằng Foo không thể có một constructor mặc định. Sau đó, làm thế nào bạn sẽ đối phó với điều đó trong tình huống này, kể từ khi chúng tôi không thể viết này nữa: Foo l_Foo.

Foo phải có hàm tạo mặc định. Ngay cả khi bạn không cung cấp cho nó, trình biên dịch phải dành cho bạn. Giả sử bạn khai báo một hàm tạo private, không có cách nào bạn có thể sử dụng phương thức gọi này. Bạn cần tạo Foo cấu hình mặc định hoặc sử dụng Secnario C.

+1

'const Foo SomeClass :: GetFoo() ':: Kịch bản A :: Tôi không trả lại tài liệu tham khảo tạm thời ở đây .. – brainydexter

+0

@brainydexter: Rất tiếc .. bạn đã đúng. Tuy nhiên, tôi tin rằng nó vẫn còn sai. Tôi không chắc chắn 100%, nhưng tôi tin rằng tạm thời sẽ bị phá hủy mặc dù có một tham chiếu đến nó. Bạn cần phải có đối tượng thực sự xung quanh. –

+1

kịch bản A là tốt nhưng thực sự không có cải thiện hơn C. Có một quy tắc đặc biệt trong C + + kéo dài thời gian sống của đối tượng tạm thời trong trường hợp này. – sellibitze

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