English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이 문서에서는 가상 함수와 그 사용 위치를 이해하게 됩니다. 또한, 완전 가상 함수와 추상 클래스도 배울 수 있습니다.
가상 함수는 기본 클래스에서의 멤버 함수로, 그 함수를 파생 클래스에서 다시 정의하고 싶은 경우 있습니다.
상세히 설명하기 전에, 왜 가상 함수가 필요한지 먼저 이해해 보겠습니다.
게임을 개발하고 있다고 가정해 보겠습니다. (예:道具: 무기)
우리는 Weapon 클래스를 생성하고, Bomb과 Gun 클래스를 각각 자신의 무기 기능을 로드하는 두 개의 클래스를 상속했습니다.
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "무기 특성을 로드하다.\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "칼의 특성을 로드하다.\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "총의 특성을 로드하다.\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
출력 결과
무기 특성을 로드합니다. 칼의 특성을 로드합니다. 총의 특성을 로드하다.
우리는 Weapon, Bomb, Gun 클래스의 세 가지 지침 대상 w, b, g를 각각 정의했습니다. 그리고, 각 객체의 loadFeatures() 멤버 함수를 호출하기 위해 다음 명령을 사용했습니다:
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
완벽한 작품!
하지만, 우리의 게임 프로젝트는 점점 더 커지고 있습니다. 그리고, 우리는 무기 기능을 로드하기 위해 별도의 Loader 클래스를 만들기로 결정했습니다.
이 클래스 Loader는 선택한 무기를 통해 무기의 다른 기능을 로드합니다.
class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } };
loadFeatures()는 특정 무기의 특성을 로드합니다.
#include <iostream> using namespace std; class Weapon { public: Weapon() { cout << "무기 특성을 로드합니다。\n"; } void features() { cout << "무기 특성을 로드합니다。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "칼의 특성을 로드합니다。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "총의 특성을 로드합니다。\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
출력 결과
무기 특성을 로드합니다. 무기 특성을 로드합니다. 무기 특성을 로드합니다. 무기 특성을 로드합니다.
우리의 구현은 올바르게 보입니다. 하지만, 무기 특성이 로드되었습니다4이유는 무엇인가요?
초기에, 무기 객체 w는 (Bomb) 클래스의 b 객체를 가리킵니다. 그리고 l 객체를 사용하여 (Loader 클래스의) 포인터를 가리키고 loadFeatures() 함수에 전달하여 Bomb 객체의 특성을 로드하려고 시도합니다.
동일하게, Gun 객체의 특성을 로드하려고 시도합니다.
하지만, Loader 클래스의 loadFeatures() 함수는 Weapon 클래스 객체의 포인터를 파라미터로 가리킵니다:
void loadFeatures(Weapon *weapon)
이것이 무기 특성이 로드된 이유입니다.4이유로 이를 해결하기 위해 virtual 키워드를 사용하여 기본 클래스(Weapon 클래스)의 가상 함수를 구현해야 합니다.
class Weapon { public: virtual void features() { cout << "무기 특성을 로드합니다。\n"; } };
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "무기 특성을 로드합니다。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "칼의 특성을 로드합니다。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "총의 특성을 로드합니다。\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
출력 결과
무기 특성을 로드합니다. 칼의 특성을 로드합니다. 무기 특성을 로드합니다. 총의 특성을 로드합니다.
또한 주의하세요, l->loadFeatures(w) 함수는 l 객체가 가리키는 객체에 따라 다른 클래스의 함수를 호출합니다.
가상 함수를 사용하여 코드가 더욱 명확하고 유연해지도록 합니다.
이 프로그램에서 "무기 특성 로드"는 두 번 출력되었습니다. 위 프로그램에 다른 코드를 추가하여 무기 특성을 한 번만 로드하도록 권장합니다.
If we want to add another weapon (such as bow), we can easily add and load its characteristics.如何添加?
class Bow : public Weapon { public: void features() { this-<Weapon::features(); cout >> "로드 아치의 특성.\n"; } };
and, add the following code in the main() function.
Bow b; w = &b; l->loadFeatures(w);
It is worth noting that we have not changed any content in the Loader class to load the characteristics of the knife.
The purpose of object-oriented programming is to divide a complex problem into several small sets. This helps to effectively understand and handle problems.
Sometimes, it is better to use inheritance only when it is better to visualize the problem.
In C ++In, you can create an abstract class that cannot be instantiated (you cannot create an object of this class). However, you can derive a class from it and instantiate an object of the derived class.
An abstract class is a base class that cannot be instantiated.
A class containing a pure virtual function is called an abstract class.
A virtual function ending with =0 is called a pure virtual function. For example,
class Weapon { public: virtual void features() = 0; };
Here, the pure virtual function is
virtual void features() = 0
and, the class Weapon is an abstract class.
#include <iostream> using namespace std; // abstract class (class that cannot be instantiated) class Shape { protected: float l; public: void getData() { cin >> l; } // pure virtual function virtual float calculateArea() = 0; }; class Square : public Shape { public: float calculateArea() { return l*l; } }; class Circle : public Shape { public: float calculateArea() { return 3.14*l*l; } }; int main() { Square s; Circle c; cout << "사각형의 길이를 입력하여 면적을 계산하려면: "; s.getData(); cout << "사각형의 면적: " << s.calculateArea(); cout << "\n입력 반지름을 원의 면적을 계산하기 위해: "; c.getData(); cout << "원의 면적: " << c.calculateArea(); return 0; }
출력 결과
사각형의 면적을 계산하기 위해 길이를 입력하세요: 4 사각형의 면적: 16 원의 면적을 계산하기 위해 반지름을 입력하세요: 5 원의 면적: 78.5
이 프로그램에서는 원시 가상 함수 virtual float area()= 0;가 Shape 클래스에서 정의되었습니다.
이 점을 주의해야 할 것은, 상속 클래스에서 기본 클래스의 원시 가상 함수를 재정의해야 한다는 것입니다. 재정의가 실패하면 상속 클래스도 추상 클래스가 됩니다.