Zwraca inteligentne wskaźniki według wartości.
Jak powiedziałeś, jeśli zwrócisz go przez odniesienie, nie zwiększysz odpowiednio liczby referencji, co stwarza ryzyko usunięcia czegoś w niewłaściwym czasie. Już samo to powinno być wystarczającym powodem, aby nie wracać przez odniesienie. Interfejsy powinny być solidne.
Kwestia kosztów jest obecnie dyskusyjna dzięki optymalizacji wartości zwracanej (RVO), więc nie poniesiesz sekwencji zwiększania-zwiększania-zmniejszania lub czegoś podobnego w nowoczesnych kompilatorach. Dlatego najlepszym sposobem na zwrócenie a shared_ptr
jest po prostu zwrócenie wartości:
shared_ptr<T> Foo()
{
return shared_ptr<T>();
};
Jest to zupełnie oczywista okazja dla nowoczesnych kompilatorów C ++ w RVO. Wiem na pewno, że kompilatory Visual C ++ implementują RVO nawet wtedy, gdy wszystkie optymalizacje są wyłączone. W przypadku semantyki przenoszenia w C ++ 11 ten problem jest jeszcze mniej istotny. (Ale jedynym sposobem na upewnienie się jest profilowanie i eksperymentowanie).
Jeśli nadal nie jesteś przekonany, Dave Abrahams ma artykuł, który przedstawia argument za zwrotem według wartości. Odtwarzam tutaj fragment; Gorąco polecam przeczytanie całego artykułu:
Bądź szczery: jak się czujesz po poniższym kodzie?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Szczerze mówiąc, chociaż powinienem wiedzieć lepiej, denerwuje mnie to. W zasadzie, kiedy get_names()
wraca, musimy skopiować a vector
z string
s. Następnie musimy skopiować go ponownie podczas inicjalizacji
names
i musimy zniszczyć pierwszą kopię. Jeśli string
w wektorze jest Ns, każda kopia może wymagać aż N + 1 alokacji pamięci i całej masy nieprzyjaznych dla pamięci podręcznej dostępów do danych, gdy zawartość ciągu jest kopiowana.
Zamiast konfrontować się z tego rodzaju niepokojem, często wracam do przekazywania odniesienia, aby uniknąć niepotrzebnych kopii:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
Niestety, takie podejście jest dalekie od ideału.
- Kod wzrósł o 150%
- Musieliśmy upaść
const
upaść, ponieważ mutujemy imiona.
- Jak lubią przypominać programiści funkcjonalni, mutacja sprawia, że kod staje się bardziej skomplikowany, co do którego można wnioskować, podważając przejrzystość referencyjną i rozumowanie równania.
- Nie mamy już ścisłej semantyki wartości dla nazw.
Ale czy naprawdę konieczne jest zepsucie naszego kodu w ten sposób, aby uzyskać wydajność? Na szczęście odpowiedź brzmi nie (a zwłaszcza nie, jeśli używasz C ++ 0x).