13.10 -- Przekazywanie i zwracanie struktur

Rozważmy pracownika reprezentowanego przez 3 luźne zmienne:

int main()
{
    int id { 1 };
    int age { 24 };
    double wage { 52400.0 };

    return 0;
}

Jeśli chcemy przekazać tego pracownika do funkcji, musimy przekazać trzy zmienne:

#include <iostream>

void printEmployee(int id, int age, double wage)
{
    std::cout << "ID:   " << id << '\n';
    std::cout << "Age:  " << age << '\n';
    std::cout << "Wage: " << wage << '\n';
}

int main()
{
    int id { 1 };
    int age { 24 };
    double wage { 52400.0 };

    printEmployee(id, age, wage);

    return 0;
}

Chociaż przekazywanie 3 indywidualnych zmiennych pracowników nie jest takie złe, rozważmy funkcję, w której musimy przekazać 10 lub 12 zmiennych pracowników. Niezależne przekazywanie każdej zmiennej byłoby czasochłonne i podatne na błędy. Dodatkowo, jeśli kiedykolwiek dodamy naszemu pracownikowi nowy atrybut (np. imię), będziemy musieli teraz zmodyfikować wszystkie deklaracje funkcji, definicje i wywołania funkcji, aby zaakceptować nowy parametr i argument!

Przekazywanie struktur (przez referencję)

Dużą zaletą używania struktur w porównaniu z pojedynczymi zmiennymi jest to, że możemy przekazać całą strukturę do funkcji, która musi współpracować z elementami. Struktury są zazwyczaj przekazywane przez referencję (zwykle przez stałą referencję), aby uniknąć tworzenia kopii.

#include <iostream>

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

void printEmployee(const Employee& employee) // note pass by reference here
{
    std::cout << "ID:   " << employee.id << '\n';
    std::cout << "Age:  " << employee.age << '\n';
    std::cout << "Wage: " << employee.wage << '\n';
}

int main()
{
    Employee joe { 14, 32, 24.15 };
    Employee frank { 15, 28, 18.27 };

    // Print Joe's information
    printEmployee(joe);

    std::cout << '\n';

    // Print Frank's information
    printEmployee(frank);

    return 0;
}

W powyższym przykładzie przekazujemy cały Employee Do printEmployee() (dwa razy, raz dla joe i raz dla frank).

Powyższy program wyprowadza:

ID:   14
Age:  32
Wage: 24.15

ID:   15
Age:  28
Wage: 18.27

Ponieważ przekazujemy cały obiekt struktury (a nie poszczególne elementy), potrzebujemy tylko jednego parametru bez względu na liczbę elementów obiektu struktury ma. A w przyszłości, jeśli kiedykolwiek zdecydujemy się dodać nowe elementy do naszej Employee struktury, nie będziemy musieli zmieniać deklaracji funkcji ani wywołania funkcji! Nowy element zostanie dołączony automatycznie.

Powiązana treść

O tym, kiedy przekazywać struktury według wartości, a kiedy referencji, mówimy w lekcji 12.6 -- Przekaż stałą lwartość odniesienia.

Przekazywanie struktur tymczasowych

W poprzednim przykładzie utworzyliśmy zmienną pracowniczą joe wcześniej do przekazania go do funkcji printEmployee() . Pozwala to na nadanie zmiennej Pracownik nazwy, która może być przydatna do celów dokumentacyjnych. Wymaga to jednak również dwóch instrukcji (jednej do utworzenia joe, drugiej do użycia joe).

W przypadkach, gdy używamy zmiennej tylko raz, konieczność nadania zmiennej nazwy i oddzielenia jej tworzenia od użycia może zwiększyć złożoność. W takich przypadkach lepszym rozwiązaniem może być użycie obiektu tymczasowego. Obiekt tymczasowy nie jest zmienna, więc nie ma ona identyfikatora.

To ten sam przykład co powyżej, ale zastąpiliśmy zmienne joe i frank obiektami tymczasowymi:

#include <iostream>

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

void printEmployee(const Employee& employee) // note pass by reference here
{
    std::cout << "ID:   " << employee.id << '\n';
    std::cout << "Age:  " << employee.age << '\n';
    std::cout << "Wage: " << employee.wage << '\n';
}

int main()
{
    // Print Joe's information
    printEmployee(Employee { 14, 32, 24.15 }); // construct a temporary Employee to pass to function (type explicitly specified) (preferred)

    std::cout << '\n';

    // Print Frank's information
    printEmployee({ 15, 28, 18.27 }); // construct a temporary Employee to pass to function (type deduced from parameter)

    return 0;
}

Możemy utworzyć obiekt tymczasowy Employee W pierwszym wywołaniu używamy składni Employee { 14, 32, 24.15 }, która nakazuje kompilatorowi utworzenie Employee obiektu i inicjalizację z dostarczonymi inicjatorami. Jest to preferowana składnia, ponieważ wyjaśnia, jaki rodzaj obiektu tymczasowego tworzymy i nie ma możliwości, aby kompilator błędnie zinterpretował nasze intencje.

W drugim wywołaniu używamy składni { 15, 28, 18.27 }. Kompilator jest na tyle inteligentny, że rozumie, że podane argumenty muszą zostać przekonwertowane na Employee , aby wywołanie funkcji zakończyło się sukcesem. więc nie będzie działać w przypadkach, gdy akceptowalne są tylko jawne konwersje.

Powiązana treść

W lekcji mówimy więcej o obiektach tymczasowych typu klasowego i konwersjach 14.13 -- Tymczasowe obiekty klas.

Kilka dodatkowych rzeczy na temat obiektów tymczasowych: są one tworzone i inicjowane w momencie definicji i niszczone na końcu pełnego wyrażenia, w którym są tworzone. Ocena obiektu tymczasowego jest wyrażeniem wartości i może być używana tylko w miejscach, w których wartości są akceptowane argument, zostanie powiązany tylko z parametrami, które akceptują wartości. Obejmuje to przekazywanie wartości i przekazywanie stałych referencji, a wyklucza przekazywanie referencji innych niż stałe i przekazywanie adresów.

Zwracanie struktur

Rozważmy przypadek, w którym mamy funkcję, która musi zwrócić punkt w trójwymiarowej przestrzeni kartezjańskiej. Taki punkt ma 3 atrybuty: współrzędną x, współrzędną y i współrzędną z. Ale funkcje mogą zwracać tylko jedną wartość. Jak więc zwrócić użytkownikowi wszystkie 3 współrzędne?

Jednym z powszechnych sposobów jest zwrócenie struktury:

#include <iostream>

struct Point3d
{
    double x { 0.0 };
    double y { 0.0 };
    double z { 0.0 };
};

Point3d getZeroPoint()
{
    // We can create a variable and return the variable (we'll improve this below)
    Point3d temp { 0.0, 0.0, 0.0 };
    return temp;
}

int main()
{
    Point3d zero{ getZeroPoint() };

    if (zero.x == 0.0 && zero.y == 0.0 && zero.z == 0.0)
        std::cout << "The point is zero\n";
    else
        std::cout << "The point is not zero\n";

    return 0;
}

Wypisuje:

The point is zero

Struktury zdefiniowane wewnątrz funkcji są zwykle zwracane przez wartość, aby nie zwracać wiszącej referencji.

W getZeroPoint() Powyższa funkcja tworzy nowy nazwany obiekt (temp), abyśmy mogli go zwrócić:

Point3d getZeroPoint()
{
    // We can create a variable and return the variable (we'll improve this below)
    Point3d temp { 0.0, 0.0, 0.0 };
    return temp;
}

Nazwa obiektu (temp) tak naprawdę nie zapewnia tutaj żadnej wartości dokumentacyjnej.

Możemy nieco ulepszyć naszą funkcję, zwracając zamiast tego obiekt tymczasowy (bez nazwy/anonimowy):

Point3d getZeroPoint()
{
    return Point3d { 0.0, 0.0, 0.0 }; // return an unnamed Point3d
}

W tym przypadku tworzony jest tymczasowy Point3d , kopiowany z powrotem do obiektu wywołującego, a następnie niszczony na końcu wyrażenia. Zwróć uwagę, o ile jest to czystsze (jedna linia zamiast dwóch i nie ma potrzeby rozumienia, czy temp jest użyte więcej niż raz).

Powiązana treść

Obiekty anonimowe omawiamy bardziej szczegółowo na lekcji 14.13 -- Tymczasowe obiekty klas.

Wydedukowanie typu zwracanego

W przypadku, gdy funkcja ma jawny typ zwracany (np. Point3d), możemy nawet pominąć typ zwrotu instrukcja:

Point3d getZeroPoint()
{
    // We already specified the type at the function declaration
    // so we don't need to do so here again
    return { 0.0, 0.0, 0.0 }; // return an unnamed Point3d
}

Uważa się to za niejawną konwersję.

Zauważ także, że ponieważ w tym przypadku zwracamy wszystkie wartości zerowe, możemy użyć pustych nawiasów klamrowych, aby zwrócić Point3d zainicjowany przez wartość:

Point3d getZeroPoint()
{
    // We can use empty curly braces to value-initialize all members
    return {};
}

Struktury są ważnym elementem konstrukcyjnym

Struktury są przydatne same w sobie, klasy (które są sercem C++ i zorientowanego obiektowo programowanie) opierają się bezpośrednio na koncepcjach, które tutaj przedstawiliśmy. Dobra znajomość struktur (zwłaszcza elementów danych, wyboru elementów i domyślnej inicjalizacji elementów) znacznie ułatwi przejście do klas.

Czas quizu

Pytanie nr 1

Prowadzisz witrynę internetową i próbujesz obliczyć przychody z reklam. Napisz program, który umożliwi wprowadzenie 3 danych:

  • Ile reklam zostało obejrzanych.
  • Jaki procent użytkowników kliknął reklamę.
  • Średnie zarobki na klikniętą reklamę.

Zapisz te 3 wartości w strukturze. Przekaż tę strukturę do innej funkcji, która wypisuje każdą z wartości. Funkcja drukowania powinna także wydrukować kwotę zarobioną danego dnia (pomnożyć przez siebie te 3 pola).

Pokaż wskazówkę

Pokaż rozwiązanie

Pytanie nr 2

Utwórz strukturę przechowującą ułamek. Struktura powinna mieć licznik będący liczbą całkowitą i element mianownika będący liczbą całkowitą.

Napisz funkcję, która wczyta ułamek od użytkownika i użyj jej do wczytania dwóch obiektów ułamkowych. Napisz inną funkcję, która pomnoży przez siebie dwa ułamki i zwróci wynik jako ułamek (nie musisz zmniejszać ułamka). Napisz inną funkcję, która wypisze ułamek.

Wyjście Twojego programu powinno być zgodne z następującymi:

Enter a value for the numerator: 1
Enter a value for the denominator: 2

Enter a value for the numerator: 3
Enter a value for the denominator: 4

Your fractions multiplied together: 3/8

Podczas mnożenia dwóch ułamków razem wynikowy licznik jest iloczynem dwóch liczników, a powstały mianownik jest iloczynem dwóch mianowników.

Pokaż rozwiązanie

Pytanie nr 3

Dlaczego w rozwiązaniu poprzedniego pytania quizu getFraction() zwraca wartość według wartości zamiast przez odniesienie?

Pokaż rozwiązanie

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
324 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze