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).
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.
Pytanie nr 3
Dlaczego w rozwiązaniu poprzedniego pytania quizu getFraction() zwraca wartość według wartości zamiast przez odniesienie?

