Programowanie w C++
Lab 9
Przeciążanie nazw funkcji i przeciążanie operatorów
1. Przeciążanie nazw funkcji
W języku C++ można zdefiniować więcej niż jedną funkcję o tej samej nazwie. Nazywa się to przeciążaniem nazw funkcji (lub w skrócie przeciążaniem funkcji). Dzięki temu funkcja o jednej nazwie może obsługiwać różne typy danych. Aby kod programu się skompilował, funkcje muszą się różnić typem oraz ewentualnie ilością argumentów wejściowych. Na tej podstawie kompilator rozróżnia, którą implementację funkcji zastosować dla danego jej wywołania w programie.
Przykład 1. Program ma trzy przeciążone funkcje o nazwie wyswietl( argument ), gdzie do funkcji jako argument możemy przekazać liczbę całkowitą (typu int), liczbę rzeczywistą (typu float) lub nawet własny typ danych typu punkt – wówczas wyświetlone zostaną obie współrzędne punktu.
W zależności od przekazanego argumentu zostanie uruchomiona odpowiednia funkcja.
Cały program:
#include <iostream>
using namespace std;
// definicja własnej struktury typu punkt // przechowuje współrzędne x i y
struct punkt {
float x, y;
};
// przeciążona funkcja wyswietl dla typu int void wyswietl(int liczba)
{
cout << "liczba typu int: " << liczba << endl;
}
// przeciążona funkcja wyswietl dla typu float void wyswietl(float liczba)
{
cout << "liczba typu float: " << liczba << endl;
}
// przeciążona funkcja wyswietl dla typu punkt void wyswietl(punkt a)
{
cout << "x: " << a.x << "\t y:" << a.y << endl;
}
int main() {
float jakas_liczba = 5.342;
int liczba_calkowita = 3;
wyswietl(jakas_liczba); // wywolanie funcji wyswietl ze zmienną typu float wyswietl(liczba_calkowita); // wywolanie funcji wyswietl ze zmienną typu int
// tworzymy dwa punkty i określamy ich współrzędne
punkt P1 = {5,4};
punkt P2 = {10,10};
// wywolujemy funkcje wyswietl dla typu punkt dla obu punktow wyswietl(P1);
wyswietl(P2);
return 0;
}
2. Przeciążanie operatorów
Przeciążanie operatorów umożliwia definiowanie działań dla własnych typów danych (struktur, obiektów) oraz na zmianę działania operatorów da wbudowanych typów (takich jak int, float, char itd.). Na razie zajmiemy się programowaniem przykładowych działań dla własnego typu. W poniższym przykładowym programie:
1) Stworzymy nowy typ wektor. Będzie to dwuwymiarowy wektor zaczepiony w początku układu współrzędnych i końcu określonym przez parę punktów x i y.
2) Przeciążymy operator przypisania = , dzięki czemu będzie można przypisać współrzędne z jednego wektora (lub działania którego wynikiem jest wektor) do innego wektora, np.
wektor w1(5, 7); // tworzymy wektor w1 o wsp. x = 5 i y = 7 wektor w2 = w1; // tworzymy wektor w2 i przypisujemy mu
// współrzędne wektora w1;
Bez przeciążania znaku = nie moglibyśmy zapisać w2 = w1. Przypisanie musielibyśmy zrealizować np. tak:
w1.x = w2.x;
w1.y = w2.y;
Jeśli mielibyśmy 10 składowych w strukturze, to wtedy mielibyśmy też 10 instrukcji
przypisania poszczególnych składowych. Kod programu niepotrzebnie się wydłuża jak i łatwo o pomyłkę. Dzięki przeciążeniom operatorów można wszystko zrealizować jedną instrukcją.
3) Przeciążymy operator dodawania + , który pozwoli na dodawanie wektorów, np.
wektor w1(5, 7), // tworzymy wektor w1 i w2 z określonymi w2(2, 2); // współrzędnymi
wektor w3 = w1 + w2; // obliczamy sumę wektorów w1 i w2 i wynik // zapisujemy do nowego wektora w3
4) Przeciążymy operator porównania == , który pozwoli na sprawdzenie, czy dwa wektory mają te same współrzędne, np.
bool czy_rowne = (w1 == w2) // wyrażenie w1 == w2 zwróci wartość // true jeśli wektory będą miały jednakowe współrzędne
5) Przeciążymy operator *, co pozwoli na skalowanie wektora, np.
wektor w1(10, 5);
wektor w2 = w1*2.5; // wektor w2 będzie miał współrzędne 25, 12.5
Cały program:
/*
Program definiuje nowy typ wektor i przeciąża dla niego operator przypisania =, dodawania +, porównania == oraz mnożenia *.
Uwaga dot. nazewnictwa (na przykładzie działania dodawania):
operator | w1 + w2 | | lewy prawy operand operand
*/
#include <iostream>
using namespace std;
// definicja nowego typu wektor za pomocą stuktury struct wektor
{
float x;
float y;
// konstruktor i wartości domyślne
// umożliwia utworzenie i zainicjowanie zmiennej w postaci np. wektor = P(5, 10);
wektor(float a=0, float b=0) {
x = a;
y = b;
}
// przeciążanie operatorów w implementacji struktury:
// składnia: zwracany_typ operator# = (operand z prawej strony)
// # - jeden z operatorów języka C++, np. +, -, /, *, %, ^, ==, !=, itd.
// można dodać const jeśli operand ma nie być modyfikowany
// korzystamy przy tym z referencji (&) aby nie tworzyć niepotrzebnie // kopii prawego operandu w pamięci
// przeciążenie operatora =
// przepisuje wsp. z wektora a do aktualnego wektora.
// Wektor a jest tym, co pojawi się z prawej stronypodczas użycia operatora = // (lewy operand)
wektor operator=(const wektor &a) {
x=a.x;
y=a.y;
}
// przeciążenie operatora +
// dodaje wspołrzędne wektora z lewej strony znaku + (lewy operand) // oraz wekroa z prawej strony znaku + (prawy operand)
// Zwraca nowy wektor
wektor operator+(const wektor &a) const {
return wektor(x+a.x, y+a.y);
}
// przeciążenie operatora porównania ==
// Zwraca wartość logiczną prawda, jeżeli składowe // porównywanych wektorów są jednakowe
bool operator==(const wektor &a) const {
return (x == a.x && y == a.y);
}
// przeciążony operator mnożenia * // Mnoży współżędne wektora przez skalę wektor operator*(const float &skala) {
return wektor(x*skala, y*skala);
} };
int main() {
// utworzenie zmiennych typu wektor: P1 i P2 // oraz zainicjowanie ich współrzędnych wektor W1(3, 4);
wektor W2(2, 5);
// --- cout << "Wspolrzedne wektora W1: " << endl;
cout << W1.x << " " << W1.y << endl;
cout << "Wspolrzedne wektora W2: " << endl;
cout << W2.x << " " << W2.y << endl;
// --- // wykorzystanie operatora sumy + oraz przypisania = wektor W3 = W1+W2;
cout << "Wspolrzedne wektora W3 = W1 + W2: " << endl;
cout << W3.x << " " << W3.y << endl;
// --- // wykorzystanie przeciążonego operatora * wektor W4 = W3*0.5;
cout << "\nSkaluje wektor W3 o wspolczynnik 0.5." << endl;
cout << "Wspolrzedne nowego wektora W4:" << endl;
cout << W4.x << " " << W4.y << endl << endl;
// --- // sprawdzenie działania operatora ==
if (W2 == W3)
cout << "wektory W2 i W2 maja jednakowe wspolrzedne.";
else
cout << "wektory W2 i W3 maja rozne wspolrzedne.";
return 0;
}
Zad 1.
Zmodyfikuj program z przykładu 1:
a) Struktura punkt niech przechowuje również trzecią współrzędną, a wywołanie funkcji wyświetl dla punktu powoduje wyświetlenie wszystkich współrzędnych na ekranie.
b) Zdefiniuj pięcioelementową tablicę elementów typu całkowitoliczbowego. Dopisz czwartą przeciążoną funkcję wyswietl, która jako argument przyjmuje tablicę i liczbę jej elementów.
Niech elementy wyświetlają się w jednej linii oddzielone spacją.
Zad 2.
Dodaj do programu z przykładu 2:
a) przeciążenie operatora odejmowania umożliwiającego odejmowanie od siebie współrzędnych punktów,
b) drugie przeciążenie operatora * umożliwiające obliczanie iloczynu skalarnego wektorów.
Zwróć uwagę na drugi operand, jaki powinna przyjmować kolejna (czyli przeciążona) funkcja przeciążenia operatora * oraz na zwracany typ.
Zad 3.
Zmodyfikuj program z poprzedniego zadania, aby umożliwiał operacje na wektorach w trzech wymiarach. Do wyświetlania współrzędnych wektora napisz własną funkcję.
Zad 4.
Zmodyfikuj operator porównania == aby sprawdzał, czy wektory są jednakowej długości (zamiast sprawdzania, czy mają jednakowe współrzędne).