2013-09-06 8 views
7

Jest a set of good rules to determine whether pass by value or const referencePrzekaż wartość lub odniesienie do const?

  • Jeśli funkcja zamierza zmienić argument, jako efekt uboczny, wziąć go przez const odniesienia.
  • Jeśli funkcja nie modyfikuje argumentu, a argumentem jest typ pierwotny , pobierz go według wartości.
  • W przeciwnym razie przyjmuj go przez odniesienie do const, z wyjątkiem następujących przypadków: : Jeśli funkcja będzie musiała mimo to wykonać kopię odwołania do stałej i , pobierz ją według wartości.

Dla konstruktora w następujący sposób, jak go określić?

class A 
{ 
public: 
    A(string str) : mStr(str) {} // here which is better, 
           // pass by value or const reference? 

    void setString(string str) { mStr = str; } // how about here? 

private: 
    string mStr; 
}; 
+2

Konstruktor jest lepszy średnio jak to, co masz z 'mStr (std :: move (str))'. Jeśli naprawdę potrzebujesz zoptymalizować go jeszcze bardziej, nadal możesz go przeciążać. – chris

+0

Musisz wziąć kopię w każdym przypadku, prawda? –

+0

@chris Myślę, że twój sposób 'move' działa również dla' setString'. Jeśli 'string' nie ma żadnego ruchu ctor i przypisania, który sposób jest lepszy? Dzięki. – user1899020

Odpowiedz

3

Artykuł na stronie internetowej jest nie dobre referencje dla oprogramowania inżynierii. (Jest również prawdopodobne, nieaktualne, zważywszy, że opowiada o semantyce ruch i jest datowane od 2003 roku)

Ogólna zasada jest prosta: przejść typy klas przez const odniesienia, i innych typów przez wartość. Istnieją wyraźne wyjątki: w standardzie zgodnie z konwencjami standardowej biblioteki jest również zwykle przekazywać iteratory i obiekty funkcjonalne według wartości.

Cała reszta to optymalizacja i nie należy jej wykonywać, dopóki profiler nie powie, że trzeba.

0

W tym przypadku lepiej jest przekazać argument przez odniesienie do stałej. Powód: string jest typem klasy, nie modyfikuje go i może być dowolnie duży.

+0

Dbasz o wyjaśnienie, dlaczego? –

+4

Nie zgadzam się; jeśli kopiujesz (lub poruszasz) argument mimo to i [chcesz prędkości, podaj wartość] (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/). – Angew

+0

@Angew, ten link wydaje się być uszkodzony. Dostaję błąd bazy danych. – patrickvacek

3

W tym konkretnym przypadku, zakładając C++ 11 i przenieś konstrukcję/przypisanie dla łańcuchów, powinieneś przyjąć argument według wartości i przenieść do członka dla konstruktora.

A::A(string str) : mStr(std::move(str)) {} 

Przypadek seter jest nieco trudniejsze i nie jestem pewien, czy naprawdę chcesz/potrzeby, aby zoptymalizować każdy kawałek to ... Jeżeli chcesz zoptymalizować najbardziej można dostarczyć dwa przeciążeń, jeden przyjmowanie wartości odniesienia rvalue, a innego przyjmowanie wartości stałej constal. W każdym razie, const odniesienia lwartość jest chyba wystarczająco dobre podejście:

void A::setString(string const& str) { mStr = str; } 

Dlaczego różnica?

W przypadku konstruktora członek nie jest jeszcze zbudowany, więc będzie musiał przydzielić pamięć. Możesz przenieść tę alokację pamięci (i faktyczne kopiowanie danych, ale to jest koszt dzierżawy) do interfejsu, tak, że jeśli wywołujący ma tymczasowy, może być przekazany bez dodatkowej alokacji pamięci.

W przypadku zlecenia rzeczy są nieco bardziej skomplikowane. Jeśli bieżący rozmiar ciągu jest wystarczająco duży, aby pomieścić nową wartość, wówczas nie jest wymagana żadna alokacja, ale jeśli ciąg znaków nie jest wystarczająco duży, wówczas trzeba będzie ponownie przydzielić. Jeśli alokacja zostanie przeniesiona do interfejsu (argument wartości zależnej), zostanie wykonana zawsze, nawet jeśli jest niepotrzebna. Jeśli alokacja jest wykonywana wewnątrz funkcji (const reference argument), wówczas dla małego zestawu przypadków (tych, dla których argument jest tymczasowy, który jest większy niż bieżący bufor), zostanie wykonany alokacja, której w przeciwnym razie można by uniknąć.

1

Zawsze lepiej jest używać komendy inicjalizacji member aby zainicjować swoje wartości, gdyż zapewnia następujące korzyści:

1) Wersja przypisania tworzy domyślnego konstruktora aby zainicjować Mstr a następnie przypisać nową wartość na wierzchołek domyślnie skonstruowanego. Użycie MIL pozwala uniknąć tej marnej konstrukcji, ponieważ argumenty na liście są używane jako argumenty konstruktora.

2) Jest to jedyne miejsce do inicjowania stałych zmiennych, chyba że są to po prostu interwole, które można wykorzystać do wyliczenia w klasie.enum T {v = 2};

3) To miejsce do inicjowania odniesień.

To, co proponuję:

#include <iostream> 
#include <string> 

class A 
{ 
    private: 
     std::string mStr; 

    public: 
     A(std::string str):mStr(str){} 

     //if you are using an older version of C++11, then use 
     //std::string &str instead 
     inline const void setString(std::string str) 
     { 
     mStr = str; 
     } 

     const std::string getString() const 
     { 
     return mStr; 
     } 
}; 

int main() 
{ 
    A a("this is a 1st test."); 

    a.setString("this is a 2nd test."); 

    std::cout<<a.getString()<<std::endl; 
} 

Wystarczy popatrzeć na to: http://www.cplusplus.com/forum/articles/17820/

nadzieję, że to czyści rzeczy dla ciebie. Cos