Nadpisywanie metod
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 51 z 104.
Przesłanianie metod występuje, gdy klasa pochodna dostarcza własną implementację metody, która już istnieje w klasie bazowej. Dzięki temu klasy pochodne mogą dostosowywać odziedziczone zachowanie, zachowując ten sam podpis metody.
Aby nadpisać metodę, po prostu zdefiniuj metodę w klasie pochodnej o dokładnie takiej samej nazwie, typie zwracanym i parametrach:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Wynik: Woof!Wersja klasy pochodnej ukrywa wersję klasy bazowej. Nadal możesz jednak uzyskać dostęp do metody klasy bazowej za pomocą operatora rozstrzygania zakresu:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Wywołaj wersję z klasy bazowej
std::cout << "Woof!" << std::endl;
}
};Ta podstawowa forma przesłaniania ma istotne ograniczenie. Gdy uzyskujesz dostęp do obiektu klasy pochodnej za pomocą wskaźnika lub referencji do klasy bazowej, wywoływana jest wersja z klasy bazowej:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Wynik: Some sound (nie Woof!)Dzieje się tak, ponieważ kompilator określa, którą metodę wywołać, na podstawie typu wskaźnika, a nie rzeczywistego typu obiektu. Aby uzyskać prawdziwy polimorfizm czasu wykonania, w którym wywoływana jest właściwa metoda na podstawie rzeczywistego obiektu, potrzebujesz funkcji wirtualnych, które omówimy na następnej lekcji.
Wyzwanie
ŁatwyZbudujmy system powiadomień, który pokazuje działanie przesłaniania metod. Utworzysz bazową klasę Notifier oraz dwa wyspecjalizowane powiadamiacze, które dostosowują sposób dostarczania wiadomości.
Rozmieścisz kod w trzech plikach:
Notifier.h: Zdefiniuj bazową klasęNotifierz:- Polem
std::string recipiento dostępie chronionym - Konstruktorem, który przyjmuje nazwę odbiorcy i ją zapisuje
- Publiczną metodą
send(const std::string& message), która wyświetla:Sending to <recipient>: <message>
- Polem
EmailNotifier.h: Zdefiniuj klasęEmailNotifier, która publicznie dziedziczy poNotifier:- Konstruktor, który przyjmuje odbiorcę i przekazuje go do klasy bazowej
- Przesłoń metodę
send(), aby najpierw wywołać wersję klasy bazowej za pomocąNotifier::send(), a następnie wyświetlić:[Email sent successfully]
main.cpp: Wczytaj dwa dane wejściowe (każde w osobnym wierszu):- Nazwę odbiorcy (ciąg znaków)
- Treść wiadomości (ciąg znaków)
Zdefiniuj klasę
SMSNotifierbezpośrednio w main.cpp, która publicznie dziedziczy poNotifier:- Konstruktor, który przyjmuje odbiorcę i przekazuje go do klasy bazowej
- Przesłoń metodę
send(), aby wyświetlała:SMS to <recipient>: <message>(całkowicie zastępując zachowanie klasy bazowej bez jej wywoływania)
Utwórz po jednym obiekcie każdego typu powiadamiacza (bazowego
Notifier,EmailNotifieriSMSNotifier), używając tego samego odbiorcy. Wywołajsend()dla każdego z nich, przekazując wiadomość z danych wejściowych. Pomiędzy kolejnymi wywołaniami wyświetl linię separatora:--- Base Notifier --- <output> --- Email Notifier --- <output> --- SMS Notifier --- <output>
Na przykład dla danych wejściowych Alice i Hello there!:
--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!Zwróć uwagę, że EmailNotifier rozszerza zachowanie klasy bazowej, najpierw wywołując metodę klasy nadrzędnej, podczas gdy SMSNotifier całkowicie je zastępuje własną implementacją. Nie zapomnij o strażnikach nagłówków i dołączeniu odpowiednich nagłówków w każdym pliku.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
// TODO: Zdefiniuj tutaj klasę SMSNotifier, która publicznie dziedziczy po Notifier
// - Konstruktor, który przekazuje recipient do klasy bazowej
// - Nadpisz send(), aby wypisywała: SMS to <recipient>: <message>
// (całkowicie zastąp zachowanie klasy bazowej, nie wywołuj send() klasy nadrzędnej)
int main() {
string recipient;
string message;
getline(cin, recipient);
getline(cin, message);
// TODO: Utwórz obiekt Notifier z recipient
// Wypisz "--- Base Notifier ---"
// Wywołaj send() z message
// TODO: Utwórz obiekt EmailNotifier z recipient
// Wypisz "--- Email Notifier ---"
// Wywołaj send() z message
// TODO: Utwórz obiekt SMSNotifier z recipient
// Wypisz "--- SMS Notifier ---"
// Wywołaj send() z message
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online