Jak przekazać obiekty do funkcji w C++?

Jestem nowy w programowaniu C++, ale mam doświadczenie w Javie. Potrzebuję wskazówek jak przekazywać obiekty do funkcji w C++.

Czy muszę przekazywać wskaźniki, referencje, czy wartości nie-wskaźniki i nie-odniesienia? Pamiętam, że w Javie nie ma takich problemów, ponieważ przekazujemy tylko zmienną, która zawiera odniesienia do obiektów.

Byłoby świetnie, gdybyś mógł również wyjaśnić, gdzie korzystać z każdej z tych opcji.

Author: Rakesh K, 2010-01-26

7 answers

Rules of thumb for C++11:

Pass według wartości, z wyjątkiem sytuacji, gdy

  1. nie potrzebujesz własności obiektu i wystarczy prosty alias, w którym to przypadku pass by const reference,
  2. musisz zmutować obiekt, w takim przypadku użyj podaj Nieconst lvalue reference,
  3. przekazujesz obiekty klas pochodnych jako klasy bazowe, w którym to przypadku musisz pass przez odniesienie. (Użyj poprzednich reguł, aby określić, czy przekazać const odniesienie, czy nie.)

Przechodzenie przez wskaźnik praktycznie nigdy nie jest zalecane. Opcjonalne parametry są najlepiej wyrażone jako boost::optional, a aliasing jest wykonywany dobrze przez odniesienie.

Semantyka ruchu C++11 sprawia, że przekazywanie i zwracanie wartości jest znacznie bardziej atrakcyjne nawet dla złożonych obiektów.


Rules of thumb for C++03:

Podaj argumenty by const Bibliografia, z wyjątkiem sytuacji, gdy

  1. mają być zmieniane wewnątrz funkcji i takie zmiany powinny być odzwierciedlane na zewnątrz, w takim przypadku pass by non - const reference
  2. funkcja powinna być wywołana bez żadnego argumentu, w którym to przypadku przekazujesz wskaźnik, aby użytkownicy mogli przekazać NULL/0/nullptr zamiast tego zastosuj poprzednią zasadę, aby określić, czy powinieneś przekazywanie wskaźnika do const argument
  3. są typu wbudowanego, który może być przekazany przez kopię
  4. mają być zmieniane wewnątrz funkcji i takie zmiany powinny nie być odbite na zewnątrz, w takim przypadku można pass by copy (alternatywą byłoby Przejście zgodnie z poprzednimi regułami i wykonanie kopii wewnątrz funkcji)

(tutaj "pass by value "nazywa się" pass by copy", ponieważ przechodzenie przez wartość zawsze tworzy kopię w C++03)


Jest coś więcej, ale te kilka zasad dla początkujących zaprowadzi cię dość daleko.

 252
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-31 02:58:41

Istnieją pewne różnice w wywoływaniu konwencji w C++ i Javie. W C++ istnieją technicznie tylko dwie konwencje: pass-by-value I pass-by-reference, z pewną literaturą obejmującą trzecią konwencję pass-by-pointer (która jest właściwie pass-by-value typu wskaźnika). Dodatkowo możesz dodać const - Ness do typu argumentu, poprawiając semantykę.

Pass by reference

Przejście przez referencję oznacza, że funkcja będzie odbieranie wystąpienia obiektu, a nie jego kopii. Odniesienie jest koncepcyjnie aliasem do obiektu, który został użyty w kontekście wywołania i nie może być null. Wszystkie operacje wykonywane wewnątrz funkcji odnoszą się do obiektu poza funkcją. Ta konwencja nie jest dostępna w języku Java ani C.

Pass by value (and pass-by-pointer)

Kompilator wygeneruje kopię obiektu w kontekście wywołania i użyje tej kopii wewnątrz funkcji. Wszystkie wykonywane operacje wewnątrz funkcji wykonuje się kopię, a nie element zewnętrzny. Jest to konwencja typów prymitywnych w Javie.

Jego specjalną wersją jest przekazanie wskaźnika (adresu-obiektu) do funkcji. Funkcja otrzymuje wskaźnik, a wszelkie operacje zastosowane do samego wskaźnika są stosowane do kopii (WSKAŹNIKA), z drugiej strony operacje zastosowane do dereferenced wskaźnik będą stosowane do instancji obiektu w tym miejscu pamięci, więc funkcja może mieć skutki uboczne. Efekt użycia pass-by-value wskaźnika do obiektu pozwoli funkcji wewnętrznej na modyfikację wartości zewnętrznych, jak w przypadku pass-by-reference, a także pozwoli na opcjonalne wartości (przekazać wskaźnik null).

Jest to konwencja używana w C, gdy funkcja musi zmodyfikować zewnętrzną zmienną, a konwencja używana w Javie z typami referencji: odniesienie jest kopiowane, ale obiekt, do którego odnosi się, jest taki sam: zmiany odniesienia / wskaźnika nie są widoczne na zewnątrz funkcji, ale zmiany w pamięci wskazanej są.

Dodawanie const do równania

W C++ można przypisywać stałe do obiektów przy definiowaniu zmiennych, wskaźników i odniesień na różnych poziomach. Możesz zadeklarować zmienną jako stałą, możesz zadeklarować odniesienie do stałej instancji i możesz zdefiniować wszystkie wskaźniki do stałych obiektów, stałe wskaźniki do zmiennych obiektów i stałe wskaźniki do stałych elementów. Odwrotnie w Javie można zdefiniować tylko jeden poziom niezmienności (ostatnie słowo kluczowe): poziom zmiennej (instancja dla typów prymitywnych, Referencja dla typów referencyjnych), ale nie można zdefiniować odniesienia do niezmiennego elementu (chyba że sama klasa jest niezmienna).

Jest szeroko stosowany w konwencjach wywołujących C++. Gdy obiekty są małe, można przekazać obiekt według wartości. Kompilator wygeneruje kopię, ale ta kopia nie jest kosztowną operacją. Dla każdego innego typu, jeśli funkcja nie zmieni obiektu, można może przekazać odwołanie do stałej instancji (Zwykle zwanej stałą referencją) typu. To nie skopiuje obiektu, ale przekaże go do funkcji. Ale jednocześnie kompilator zagwarantuje, że obiekt nie zostanie zmieniony wewnątrz funkcji.

Rules of thumb

Oto kilka podstawowych zasad, których należy przestrzegać:

  • preferuj wartość pass-by-value dla prymitywnych typów
  • preferuj odniesienie pass-by-reference z odniesieniami do stałych dla innych typów
  • Jeśli funkcja musi zmodyfikować argument use pass-by-reference
  • jeśli argument jest opcjonalny, użyj wskaźnika pass-by-pointer (do stałej, jeśli wartość opcjonalna nie powinna być modyfikowana)

Istnieją inne małe odstępstwa od tych zasad, z których pierwszym jest obchodzenie się z własnością obiektu. Gdy obiekt jest dynamicznie przydzielany z new, musi być dealokowany za pomocą delete (lub ich wersji []). Przedmiot lub funkcja, która jest odpowiedzialna za zniszczenie obiekt jest uważany za właściciela zasobu. Gdy dynamicznie alokowany obiekt jest tworzony w kawałku kodu, ale własność jest przenoszona na inny element, zwykle odbywa się to za pomocą semantyki pass-by-pointer lub, jeśli to możliwe, za pomocą inteligentnych wskaźników.

Side note

Ważne jest, aby podkreślić znaczenie różnicy między odniesieniami do C++ i Javy. W C++ odwołania są koncepcyjnie instancją obiektu, a nie dostępnikiem do niego. Najprostszy przykładem jest implementacja funkcji swap:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

Funkcja swap powyżej zmienia oba swoje argumenty poprzez użycie referencji. Najbliższy kod w Javie:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

Wersja kodu Java zmodyfikuje kopie odniesień wewnętrznie, ale nie zmodyfikuje rzeczywistych obiektów zewnętrznie. Odniesienia Java są wskaźnikami C bez arytmetyki wskaźników, które są przekazywane przez wartość do funkcji.

 102
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-26 14:59:05

Jest kilka spraw do rozważenia.

Parametr zmodyfikowany (parametry" out "I" in/out")

void modifies(T &param);
// vs
void modifies(T *param);

W tym przypadku chodzi głównie o styl: czy chcesz, aby Kod wyglądał jak call (obj) lub call (&obj)? Istnieją jednak dwa punkty, w których różnica ma znaczenie: opcjonalny przypadek, poniżej, i chcesz użyć odniesienia podczas przeciążania operatorów.

...i opcjonalne

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);

Parametr nie zmodyfikowany

void uses(T const &param);
// vs
void uses(T param);

To jest interesujące case. Zasada jest taka ,że typy "tanie w kopiowaniu" są przekazywane przez wartość - są to zazwyczaj małe typy ( ale nie zawsze) - podczas gdy inne są przekazywane przez const ref. Jeśli jednak musisz wykonać kopię w swojej funkcji niezależnie od tego, powinieneś przekazać wartość . (Tak, to ujawnia trochę szczegółów implementacji. C ' est le C++.)

...i opcjonalne

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

Jest tu najmniejsza różnica między wszystkimi sytuacjami, więc wybierz to, co sprawia, że Twoje życie najłatwiej.

Const by value jest szczegółem implementacji

void f(T);
void f(T const);

Te deklaracje są w rzeczywistości dokładnie tą samą funkcją! podczas przekazywania przez wartość, const jest wyłącznie szczegółem implementacji. Wypróbuj:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types
 22
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-19 15:00:46

Pass by value:

void func (vector v)

Przekazuje zmienne według wartości, gdy funkcja wymaga całkowitej izolacji od środowiska, tzn. aby zapobiec modyfikowaniu przez funkcję oryginalnej zmiennej, jak również aby uniemożliwić innym wątkom modyfikowanie jej wartości podczas wykonywania funkcji.

Minusem są cykle procesora i dodatkowa pamięć zużyta na skopiowanie obiektu.

Podaj const reference:

void func (const vector & v);

Ten formularz emuluje zachowanie pass-by-value podczas usuwania kopiowanie nad głową. Funkcja uzyskuje dostęp do odczytu do oryginalnego obiektu, ale nie może zmienić jego wartości.

Minusem jest bezpieczeństwo wątku: wszelkie zmiany wprowadzone do oryginalnego obiektu przez inny wątek pojawią się wewnątrz funkcji podczas jej wykonywania.

Podaj nie-const reference:

void func (vector & v)

Użyj tego, gdy funkcja musi zapisać jakąś wartość do zmiennej, która ostatecznie zostanie użyta przez wywołującego.

Podobnie jak w przypadku const reference, nie jest to nici bezpieczne.

Podaj wskaźnik const:

void func (const vector * vp);

Funkcjonalnie tak samo jak pass by const-reference z wyjątkiem innej składni, plus fakt, że funkcja wywołująca może przekazać wskaźnik NULL, aby wskazać, że nie ma ważnych danych do przekazania.

Nie thread safe.

Podaj wskaźnik non-const:

void func (vector * vp);

Podobne do odniesienia non-const. Wywołujący zazwyczaj ustawia zmienną na NULL, gdy funkcja nie powinna odpisywać wartości. Konwencja ta jest widoczna w wielu glibc API. Przykład:

void func (string * str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

Tak jak wszystkie przechodzą przez odniesienie / wskaźnik, a nie Bezpieczne wątki.

 15
Author: nav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-17 18:08:43

Ponieważ nikt nie wspomniał, że dodaję do niego, kiedy przekazujesz obiekt do funkcji w c++, domyślny Konstruktor kopiujący obiektu jest wywoływany, jeśli nie masz takiego, który tworzy klon obiektu, a następnie przekazuje go do metody, więc gdy zmienisz wartości obiektu, które będą odzwierciedlać kopię obiektu zamiast oryginalnego obiektu, to jest problem w c++, więc jeśli uczynisz wszystkie atrybuty klasy wskaźnikami, wtedy konstruktory kopiujące skopiują adresy wskaźnika atrybuty, więc gdy metoda wywołuje obiekt, który manipuluje wartościami przechowywanymi w atrybutach wskaźnika adresy, zmiany odzwierciedlają również w oryginalnym obiekcie, który jest przekazywany jako parametr, więc może to zachowywać się tak samo Java, ale nie zapomnij, że wszystkie atrybuty klasy muszą być wskaźnikami, również należy zmienić wartości wskaźników, będzie dużo jasne z wyjaśnieniem kodu.

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

Ale to nie jest dobry pomysł, ponieważ skończysz pisząc dużo kodu związanego z wskaźniki, które są podatne na wycieki pamięci i nie zapomnij wywołać destruktorów. Aby tego uniknąć, C++ ma konstruktory kopiujące, w których utworzysz nową pamięć, gdy obiekty zawierające wskaźniki zostaną przekazane do argumentów funkcji, które przestaną manipulować danymi innych obiektów, Java przekazuje wartość, a wartość jest referencją, więc nie wymaga konstruktorów kopiujących.

 0
Author: murali krish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-14 11:55:11

Istnieją trzy metody przekazywania obiektu do funkcji jako parametru:

  1. Pass by reference
  2. pass by value
  3. Dodawanie stałej w parametrze

Przejdź przez następujący przykład:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

Wyjście:

Say I am in someFunc
Wartość wskaźnika wynosi -17891602
Wartość zmiennej to 4

 -1
Author: amerr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 14:24:09

Poniżej przedstawiono sposoby przekazywania argumentów / parametrów do funkcji w C++.

1. według wartości.

// passing parameters by value . . .

void foo(int x) 
{
    x = 6;  
}

2. przez odniesienie.

// passing parameters by reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  
}

// passing parameters by const reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  // compile error: a const reference cannot have its value changed!
}

3. według obiektu.

class abc
{
    display()
    {
        cout<<"Class abc";
    }
}


// pass object by value
void show(abc S)
{
    cout<<S.display();
}

// pass object by reference
void show(abc& S)
{
    cout<<S.display();
}
 -1
Author: Yogeesh H T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-04 06:24:06