가상 함수

Virtual function

객체지향 프로그래밍, C++Object Pascal과 같은 언어에서 가상 함수나 가상 메서드동적 디스패치가 촉진되는 상속 가능하고 재정의 가능한 함수방법이다.이 개념은 객체지향 프로그래밍(OOOP)의 (런타임) 다형성 부분에서 중요한 부분이다.요컨대 가상 함수는 실행할 대상 함수를 정의하지만 컴파일 시 대상을 알 수 없을 수 있다.

자바스크립트, PHP, 파이톤과 같은 대부분의 프로그래밍 언어는 모든 방법을 기본적으로[1][2] 가상으로 취급하며 이러한 동작을 변경하는 수식어를 제공하지 않는다.그러나 일부 언어는 파생 클래스(Java[3]PHP[4] 최종 키워드 등)에 의해 메서드가 재정의되지 않도록 수식어를 제공한다.

목적

가상 기능의 개념은 다음과 같은 문제를 해결한다.

객체 지향 프로그래밍에서, 파생 클래스가 기본 클래스에서 상속될 때, 파생 클래스의 객체는 파생 클래스 유형 대신 기본 클래스 유형의 포인터참조를 통해 참조될 수 있다.파생 클래스에 의해 재정의된 기본 클래스 메서드가 있는 경우, 그러한 참조나 포인터에 의해 실제로 호출되는 메서드는 포인터 또는 참조의 선언된 유형에 따라 '초기'( 컴파일러에 의해) 또는 '후기'(언어의 런타임 시스템에 의해)로 바인딩(연결)될 수 있다(즉, 참조).d to.

가상 기능이 '늦음'으로 해결된다.만일 문제의 기능이 베이스 클래스에서 '가상'인 경우, 포인터나 참조의 선언된 형식에 관계없이, 가장 파생된 클래스의 기능 구현은 언급된 객체의 실제 유형에 따라 호출된다.'가상'이 아닌 경우에는 포인터나 참조의 신고된 유형에 따라 메소드가 '조기'로 확인되고 선택된다.

가상 기능은 프로그램이 코드를 컴파일하는 순간 반드시 존재하지도 않는 메서드를 호출할 수 있게 한다.[citation needed]

C++에서는 가상 메서드를 미리 지정하여virtual기본 클래스에 있는 함수의 선언 키워드.이 수식어는 파생된 클래스에서 그 방법의 모든 구현에 의해 계승되는데, 이는 그들이 계속해서 서로를 과대평가하고 늦게 구속될 수 있다는 것을 의미한다.그리고 베이스 클래스 소유의 메서드가 가상 메서드를 호출하더라도, 대신 파생 메서드를 호출할 것이다.과부하는 한 클래스에 있는 두 개 이상의 메서드가 메서드 이름은 같지만 파라미터는 다를 때 발생한다.재정의란 메서드 이름과 매개 변수가 동일한 두 가지 메서드를 갖는 것을 의미한다.과부하를 함수 매칭이라고도 하며, 오버라이드는 동적 함수 매핑이라고도 한다.

C++

동물의 계급도

예를 들어, 기본 클래스Animal가상 기능이 있을 수 있음Eat. 서브클래스Llama실행될 것이다Eat서브클래스와는 다른Wolf, 그러나 호출할 수 있다.EatAnimal로 불리는 어떤 계급의 예에 의해서, 그리고 그것을 얻는다.Eat특정 하위 클래스의 동작

계급 애니멀 {  공중의:   // 의도적으로 가상이 아닌 경우:   공허하게 하다 이동하다() {     찌꺼기::뻐드렁니가 나다 << "이 동물은 어떤 식으로든 움직인다." << 찌꺼기::끝을 맺다;   }   가상의 공허하게 하다 먹다() = 0; };  // "동물" 클래스는 원하는 경우 Eat에 대한 정의를 가질 수 있다. 계급 라마 : 공중의 애니멀 {  공중의:   // 비가상 함수 Move는 상속되지만 재정의되지는 않음.   공허하게 하다 먹다() 무효로 하다 {     찌꺼기::뻐드렁니가 나다 << "라마는 풀을 먹는다!" << 찌꺼기::끝을 맺다;   } }; 

이것은 프로그래머가 클래스 오브젝트 목록을 처리할 수 있게 한다.Animal, 차례대로 (전화로) 먹으라고 말함Eat() 목록에 어떤 종류의 동물이 있을지, 각 동물이 어떻게 먹는지, 또는 가능한 동물 유형의 전체 세트가 무엇인지 알 필요가 없다.

C에서 가상 기능의 이면에 있는 메커니즘은 다음과 같은 방법으로 제공될 수 있다.

#include <stdio.h>  /* 객체가 클래스를 가리키고 있음...*/ 구조상의 애니멀 {     경시하다 구조상의 애니멀클래스 * 계급; };  /* 가상 기능 Animal을 포함하는.먹어 */ 구조상의 애니멀클래스 {     공허하게 하다 (*먹다)(구조상의 애니멀 *); // '가상' 함수 };  /* Animal 때부터.이동이 가상 기능이 아님 그것은 위의 구조에 있지 않다.*/ 공허하게 하다 이동하다(구조상의 애니멀 * 자아의) {     활자화하다("< %p 동물>은 어떤 식으로든 움직였다.\n", (공허하게 하다 *) 자아의); }  /* Animal을 실행하는 Move와는 달리.직접 이동, 식사는 컴파일 시간에 어떤 기능을 불러올지 알 수 없다. 애니멀Eat은 Eat이 호출될 때만 런타임에 해결될 수 있다.*/ 공허하게 하다 먹다(구조상의 애니멀 * 자아의) {     경시하다 구조상의 애니멀클래스 * 계급 = *(경시하다 공허하게 하다 **) 자아의;     만일 (계급->먹다)          계급->먹다(자아의); // Animal을 실행하십시오.먹다     다른         인쇄물(더부룩한, "먹이가 구현되지 않음\n"); }  /* 라마의 이행.먹기 이것이 목표함수다. 'void Eat(동물 *를 짓는다)' */ 정태의 공허하게 하다 _Lama_eat(구조상의 애니멀 * 자아의) {     활자화하다("< %p에 있는 라마의 풀 먹자!\n", (공허하게 하다 *) 자아의);     }  /* 클래스 초기화 */ 경시하다 구조상의 애니멀클래스 애니멀 = {(공허하게 하다 *) 0}; // 기본 클래스는 Animal을 구현하지 않는다.먹다 경시하다 구조상의 애니멀클래스 라마 = {_Lama_eat};  // 그러나 파생 클래스는  인트로 본래의(공허하게 하다) {    /* 객체를 클래스의 인스턴스로 초기화 */    구조상의 애니멀 동물의 = {& 애니멀};    구조상의 애니멀 라마 = {& 라마};    이동하다(& 동물의); // 동물.이동하다    이동하다(& 라마);  // 라마.이동하다    먹다(& 동물의);  // Animal을 해결할 수 없음.그렇게 먹어서 "Not Implemented"를 stderr에 인쇄    먹다(& 라마);   // 라마를 해결한다.먹고 실행하다 } 

추상 클래스 및 순수한 가상 기능

순수 가상 함수 또는 순수 가상 방법은 파생 클래스가 추상적이지 않을 경우 파생 클래스에 의해 구현되어야 하는 가상 함수다.순수한 가상의 방법을 포함하는 클래스는 "추상"이라고 불리며 직접적으로 인스턴스화할 수 없다.추상 클래스의 하위 클래스는 모든 상속된 순수 가상 방법이 해당 클래스 또는 상위 클래스에 의해 구현된 경우에만 직접 인스턴스화될 수 있다.순수한 가상 방법에는 일반적으로 선언(서명)이 있고 정의(이행)가 없다.

예를 들어 추상 기본 클래스MathSymbol순수한 가상 기능을 제공할 수 있음doOperation(), 및 파생 클래스Plus그리고Minus실행하다doOperation()구체적인 구현을 제공한다.구현doOperation()이치에 맞지 않다MathSymbol로서 분류하다.MathSymbol추상적인 개념이며, 각각의 종류의 (하위 클래스)에 대해서만 행동이 정의된다.MathSymbol. 이와 유사하게, 의 지정된 하위 클래스MathSymbol의 실행 없이는 완성되지 않을 것이다.doOperation().

순수 가상 방법은 일반적으로 이를 선언하는 클래스에 구현되지 않지만, 일부 언어의 순수 가상 방법(예: C++ 및 Python)은 선언 클래스에 구현을 포함하도록 허용되며, 파생 클래스가 적절한 경우 위임할 수 있는 예비 또는 기본 동작을 제공한다.[5][6]

인터페이스를 정의하기 위해 메서드 선언이 사용되는 곳에서도 순수한 가상 기능을 사용할 수 있다. 이는 자바의 인터페이스 키워드가 명시적으로 지정하는 것과 유사하다.그러한 용도에서는 파생 클래스가 모든 구현을 제공할 것이다.그러한 설계 패턴에서 인터페이스의 역할을 하는 추상 클래스는 순수한 가상 기능만 포함할 뿐 데이터 구성원이나 일반적인 방법은 포함할 수 없다.C++에서는 C++가 다중 상속을 지원하기 때문에 순수하게 추상적인 클래스를 인터페이스로 사용하는 것이 효과가 있다.그러나 많은 OOP 언어가 다중 상속을 지원하지 않기 때문에 별도의 인터페이스 메커니즘을 제공하는 경우가 많다.예를 들어 자바 프로그래밍 언어가 있다.

건설 및 파괴 시 거동향

언어는 개체의 생성자소멸자가 실행되는 동안 동작이 다르다.이러한 이유로, 시공사에서의 가상 기능을 호출하는 것은 일반적으로 금지된다.

C++에서는 "베이스" 함수를 호출한다.구체적으로는 현재의 생성자의 클래스보다 더 많이 파생되지 않는 가장 파생된 함수를 호출한다.[7]만약 그 함수가 순수한 가상 함수라면, 정의되지 않은 행동이 발생한다.[8]클래스에 그러한 순수한 가상 기능에 대한 구현이 포함되어 있더라도 이는 사실이다.컴파일 시간이나 링크 시간에 순수한 가상 기능에 대한 간접적인 호출을 감지하기 위해 C++를 준수할 필요는 없다(일반적으로 불가능).일부 런타임 시스템런타임에 순수한 가상 함수에 대한 호출을 경험할 때 순수 가상 함수 호출 오류를 발생시킨다.

자바와 C#에서는 파생된 구현을 호출하지만, 파생된 생성자에 의해 아직 초기화되지 않은 필드도 있다(기본값 0으로 초기화되지만).[9]추상적 공장 패턴과 같은 일부 설계 패턴은 이 능력을 지원하는 언어에서 이 사용을 적극적으로 촉진한다.

가상 소멸자

객체 지향 언어는 일반적으로 객체가 생성되고 파괴될 때 자동으로 메모리 할당과 할당 해제를 관리한다.그러나 일부 객체 지향 언어에서는 원하는 경우 사용자 정의 소멸자 방법을 구현할 수 있다.해당 언어가 자동 메모리 관리를 사용하는 경우, 호출되는 사용자 정의 소멸자(일반적으로 이 맥락에서 파이널라이저라고 함)가 해당 객체에 적합한 것으로 확실하다.예를 들어 Animal을 계승하는 Wolf 유형의 물체가 만들어지고, 둘 다 관습 파괴자를 가지고 있다면, 호출된 물체는 Wolf에서 선언된 물체가 될 것이다.

수동 메모리 관리 맥락에서 상황은 특히 정적 전송과 관련하여 복잡할 수 있다.만약 Wolf 타입의 물체가 생성되었지만 Animal pointer가 가리키고 있는 것이 이 Animal pointer 타입이고 삭제된 것이 이 Animal pointer 타입이라면, 실제로 불리는 소멸자는 Wolf에 대해 정의된 것이 아니라 Animal에 대해 정의된 것일 수도 있다, 파괴자가 가상이 아니라면 말이다.특히 파괴자가 가상이 아닌 경우 동작이 프로그래밍 오류의 일반적인 원인이 되는 C++의 경우가 이에 해당한다.

참고 항목

참조

  1. ^ "Polymorphism (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance)". docs.oracle.com. Retrieved 2020-07-11.
  2. ^ "9. Classes — Python 3.9.2 documentation". docs.python.org. Retrieved 2021-02-23.
  3. ^ "Writing Final Classes and Methods (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance)". docs.oracle.com. Retrieved 2020-07-11.
  4. ^ "PHP: Final Keyword - Manual". www.php.net. Retrieved 2020-07-11.
  5. ^ 순수 가상 소멸자 - cppreference.com
  6. ^ "abc - 추상 기본 클래스: @abc.abstractmethod"
  7. ^ Meyers, Scott (June 6, 2005). "Never Call Virtual Functions during Construction or Destruction".
  8. ^ "N4659: Working Draft, Standard for Programming Language C++" (PDF). §13.4.
  9. ^ Ganesh, S.G. (August 1, 2011). "Joy of Programming: Calling Virtual Functions from Constructors".