답변:
에서 위키 백과의 가상 기능 ...
C ++ 및 Object Pascal과 같은 언어로 된 객체 지향 프로그래밍에서 가상 함수 또는 가상 메소드는 상속 및 재정의 가능한 함수 또는 동적 디스패치가 용이 한 메소드입니다. 이 개념은 객체 지향 프로그래밍 (OOP)의 (런타임) 다형성 부분에서 중요한 부분입니다. 즉, 가상 함수는 실행할 대상 함수를 정의하지만 컴파일시 대상을 알 수 없습니다.
비가 상 함수와 달리 가상 함수가 재정의되면 가장 파생 된 버전은 생성 된 수준이 아니라 클래스 계층의 모든 수준에서 사용됩니다. 따라서 기본 클래스의 한 메소드가 가상 메소드를 호출 하면 기본 클래스에 정의 된 버전 대신 파생 클래스에 정의 된 버전이 사용됩니다.
이는 파생 클래스에서 여전히 재정의 될 수있는 비가 상 함수와 대조적이지만 "새"버전은 파생 클래스와 그 이하에서만 사용되지만 기본 클래스의 기능은 전혀 변경되지 않습니다.
이므로..
순수 가상 함수 또는 순수 가상 메소드는 파생 클래스가 추상이 아닌 경우 파생 클래스에서 구현해야하는 가상 함수입니다.
순수한 가상 메소드가 존재하면 클래스는 "추상적"이며 자체적으로 인스턴스화 할 수 없습니다. 대신 순수 가상 메소드를 구현하는 파생 클래스를 사용해야합니다. 순수 가상은 기본 클래스에 전혀 정의되어 있지 않으므로 파생 클래스 가이를 정의 해야합니다 . 그렇지 않으면 파생 클래스도 추상적이고 인스턴스화 할 수 없습니다. 추상 메소드가없는 클래스 만 인스턴스화 할 수 있습니다.
가상은 기본 클래스의 기능을 재정의하는 방법을 제공하며 순수 가상 에는 필수입니다.
pure
키워드 를 추가하고 싶다고 말 했지만 Bell Labs는 C ++의 주요 릴리스를 만들려고했으며 그의 관리자는 그 말기에는이를 허용하지 않았습니다. 키워드를 추가하는 것이 중요합니다.
여기 여러 곳에서 반복되는 Wikipedia의 가상 정의에 대해 언급하고 싶습니다. Wikipedia는 가상 메소드를 서브 클래스에서 재정의 할 수있는 가상 메소드로 정의했다. [다행스럽게도 위키 백과는 그 이후로 편집되었으며 이제는이를 올바르게 설명합니다.] 잘못된 것입니다. 가상 메서드뿐만 아니라 모든 메서드를 하위 클래스에서 재정의 할 수 있습니다. 가상은 다형성, 즉 런타임에 가장 파생 된 메서드 재정의를 선택할 수있는 기능을 제공하는 것 입니다.
다음 코드를 고려하십시오.
#include <iostream>
using namespace std;
class Base {
public:
void NonVirtual() {
cout << "Base NonVirtual called.\n";
}
virtual void Virtual() {
cout << "Base Virtual called.\n";
}
};
class Derived : public Base {
public:
void NonVirtual() {
cout << "Derived NonVirtual called.\n";
}
void Virtual() {
cout << "Derived Virtual called.\n";
}
};
int main() {
Base* bBase = new Base();
Base* bDerived = new Derived();
bBase->NonVirtual();
bBase->Virtual();
bDerived->NonVirtual();
bDerived->Virtual();
}
이 프로그램의 출력은 무엇입니까?
Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.
파생은 가상의 방법뿐만 아니라 가상이 아닌 Base의 모든 방법을 재정의합니다.
Base-pointer-to-Derived (bDerived)가있을 때 비가 상을 호출하면 Base 클래스 구현이 호출됩니다. 이것은 컴파일 타임에 해결됩니다. 컴파일러는 bDerived가 Base *이고 NonVirtual이 가상이 아니므로 Base 클래스에서 확인을 수행합니다.
그러나 Virtual을 호출하면 Derived 클래스 구현이 호출됩니다. 키워드 virtual 때문에 메소드 선택은 컴파일 타임 이 아닌 런타임에 발생합니다 . 컴파일 타임에 여기에서 일어나는 일은 컴파일러가 이것이 Base *이고 가상 메서드를 호출한다는 것을 알기 때문에 Base 클래스 대신 vtable에 대한 호출을 삽입한다는 것입니다. 이 vtable은 런타임에 인스턴스화되므로 런타임 분석은 가장 파생 된 대체에 대한 것입니다.
나는 이것이 너무 혼란스럽지 않기를 바랍니다. 즉, 모든 메소드를 재정의 할 수 있지만 가상 메소드 만 다형성, 즉 가장 많이 파생 된 대체의 런타임 선택을 제공합니다. 그러나 실제로 비가 상 방법을 재정의하는 것은 나쁜 습관으로 간주되며 거의 사용되지 않으므로 많은 사람들 (Wikipedia 기사를 작성한 사람 포함)은 가상 방법 만 재정의 할 수 있다고 생각합니다.
Derived*
동일한 기능 호출로을 추가 하여 포인트를 운전하는 것이 도움이 될 수 있습니다 . 그렇지 않으면 큰 대답
virtual 키워드는 C ++에 다형성을 지원할 수있는 기능을 제공합니다. 다음과 같은 클래스의 객체에 대한 포인터가있는 경우 :
class Animal
{
public:
virtual int GetNumberOfLegs() = 0;
};
class Duck : public Animal
{
public:
int GetNumberOfLegs() { return 2; }
};
class Horse : public Animal
{
public:
int GetNumberOfLegs() { return 4; }
};
void SomeFunction(Animal * pAnimal)
{
cout << pAnimal->GetNumberOfLegs();
}
이 예제에서, GetNumberOfLegs () 함수는 호출 된 객체의 클래스를 기반으로 적절한 숫자를 반환합니다.
이제 'SomeFunction'기능을 고려하십시오. Animal에서 파생되는 한 어떤 종류의 동물 개체가 전달되는지는 중요하지 않습니다. 컴파일러는 기본 클래스이므로 Animal 파생 클래스를 Animal에 자동으로 캐스트합니다.
우리가 이렇게하면 :
Duck d;
SomeFunction(&d);
'2'를 출력합니다. 우리가 이렇게하면 :
Horse h;
SomeFunction(&h);
'4'를 출력합니다. 우리는 이것을 할 수 없습니다 :
Animal a;
SomeFunction(&a);
GetNumberOfLegs () 가상 함수가 순수하기 때문에 컴파일되지 않기 때문에 클래스 (서브 클래스)를 파생시켜 구현해야합니다.
순수한 가상 함수는 주로 다음을 정의하는 데 사용됩니다.
a) 추상 수업
이것들은 그것들에서 파생 된 다음 순수 가상 함수를 구현해야하는 기본 클래스입니다.
b) 인터페이스
이들은 모든 함수가 순수한 가상 인 '빈'클래스이므로 모든 함수를 파생시킨 다음 구현해야합니다.
C ++ 클래스에서 virtual 은 메소드가 서브 클래스를 대체 (즉, 구현) 할 수 있음을 지정하는 키워드입니다. 예를 들면 다음과 같습니다.
class Shape
{
public:
Shape();
virtual ~Shape();
std::string getName() // not overridable
{
return m_name;
}
void setName( const std::string& name ) // not overridable
{
m_name = name;
}
protected:
virtual void initShape() // overridable
{
setName("Generic Shape");
}
private:
std::string m_name;
};
이 경우 서브 클래스는 initShape 함수를 재정 의하여 특수한 작업을 수행 할 수 있습니다 .
class Square : public Shape
{
public:
Square();
virtual ~Square();
protected:
virtual void initShape() // override the Shape::initShape function
{
setName("Square");
}
}
순수 가상 이라는 용어 는 서브 클래스에 의해 구현되어야하고 기본 클래스에 의해 구현되지 않은 가상 함수를 말합니다. virtual 키워드 를 사용 하고 메소드 선언 끝에 = 0 을 추가 하여 메소드를 순수 가상으로 지정합니다 .
따라서 Shape :: initShape를 순수 가상으로 만들려면 다음을 수행하십시오.
class Shape
{
...
virtual void initShape() = 0; // pure virtual method
...
};
클래스에 순수한 가상 메소드를 추가하면 클래스를 추상 기본 클래스로 만들어 인터페이스를 구현에서 분리하는 데 매우 편리합니다.
m_name
. 무슨 m_
뜻입니까?
"가상"은 서브 클래스에서 메소드가 대체 될 수 있지만 기본 클래스에서 직접 호출 가능한 구현을 가지고 있음을 의미합니다. "Pure virtual"은 직접 호출 가능한 구현이없는 가상 방법임을 의미합니다. 이러한 메소드 는 상속 계층 구조에서 적어도 한 번 재정의 해야합니다 . 클래스에 구현되지 않은 가상 메소드가 있으면 해당 클래스의 오브젝트를 구성 할 수 없어 컴파일에 실패합니다.
@quark은 순수 가상 메소드 는 구현을 가질 수 있지만 순수 가상 메소드를 대체해야하므로 기본 구현을 직접 호출 할 수는 없습니다. 다음은 기본값을 가진 순수 가상 방법의 예입니다.
#include <cstdio>
class A {
public:
virtual void Hello() = 0;
};
void A::Hello() {
printf("A::Hello\n");
}
class B : public A {
public:
void Hello() {
printf("B::Hello\n");
A::Hello();
}
};
int main() {
/* Prints:
B::Hello
A::Hello
*/
B b;
b.Hello();
return 0;
}
의견에 따르면 컴파일 실패 여부는 컴파일러마다 다릅니다. GCC 4.3.3 이상에서는 컴파일되지 않습니다.
class A {
public:
virtual void Hello() = 0;
};
int main()
{
A a;
return 0;
}
산출:
$ g++ -c virt.cpp
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note: because the following virtual functions are pure within ‘A’:
virt.cpp:3: note: virtual void A::Hello()
가상 키워드는 어떻게 작동합니까?
Man이 기본 클래스라고 가정하고 Indian은 man에서 파생됩니다.
Class Man
{
public:
virtual void do_work()
{}
}
Class Indian : public Man
{
public:
void do_work()
{}
}
do_work ()를 가상으로 선언하는 것은 단순히 다음을 의미합니다. 호출 할 do_work ()는 런타임에만 결정됩니다.
내가한다고 가정 해 봅시다.
Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.
virtual을 사용하지 않으면 호출하는 객체에 따라 컴파일러가 정적으로 결정하거나 정적으로 바인딩됩니다. 따라서 Man 객체가 do_work ()를 호출하면 Man 's do_work ()는 인디언 객체에 대한 포인트를 가지더라도
나는 가장 많이 투표 된 답변이 오도라고 생각합니다-가상이 파생 클래스에서 재정의 된 구현을 가질 수 있는지 여부에 관계없이 모든 방법. C ++에 대한 특정 참조를 사용하면 관련 함수의 런타임 (가상이 사용될 때) 바인딩 및 컴파일 시간 (가상이 사용되지 않지만 메소드가 재정의되고 기본 포인터가 파생 된 객체를 가리킬 때) 바인딩이 달라집니다.
또 다른 오해의 소지가있는 것 같습니다.
"Justin, 'pure virtual'은"이 함수는 기본 클래스로는 구현할 수 없습니다. "
이것은 잘못입니다! 순전히 가상 함수는 본문을 가질 수 있으며 구현 될 수 있습니다! 진실은 추상 클래스의 순수한 가상 함수를 정적으로 호출 할 수 있다는 것입니다! Bjarne Stroustrup과 Stan Lippman은 언어를 썼기 때문에 두 명의 훌륭한 저자입니다.
정적 메소드 바인딩을 기본적으로 사용하는 Simula, C ++ 및 C #은 프로그래머가 특정 메소드가 가상 바인딩으로 동적 바인딩을 사용하도록 지정할 수 있습니다. 동적 메소드 바인딩은 객체 지향 프로그래밍의 핵심입니다.
객체 지향 프로그래밍에는 캡슐화, 상속 및 동적 메소드 바인딩의 세 가지 기본 개념이 필요합니다.
캡슐화를 통해 추상화의 구현 세부 사항을 간단한 인터페이스 뒤에 숨길 수 있습니다.
상속을 통해 새로운 추상화를 기존 추상화의 확장 또는 개선으로 정의하여 그 특성의 일부 또는 전부를 자동으로 얻을 수 있습니다.
동적 메소드 바인딩을 사용하면 이전 추상화가 필요한 컨텍스트에서 사용될 때도 새 추상화가 새 동작을 표시 할 수 있습니다.
가상 메서드는 클래스를 파생시켜 재정의 할 수 있지만 기본 클래스 (재정의되는 클래스)에서 구현이 필요합니다.
순수한 가상 메소드는 기본 클래스를 구현하지 않습니다. 파생 클래스로 정의해야합니다. 재정의 할 것이 없기 때문에 기술적으로 재정의 된 것이 올바른 용어가 아닙니다.
파생 클래스가 기본 클래스의 메소드를 대체 할 때 가상은 기본 Java 동작에 해당합니다.
순수 가상 메소드는 추상 클래스 내에서 추상 메소드의 동작에 해당합니다. 순수한 가상 메소드와 상수 만 포함하는 클래스는 인터페이스에 대한 cpp-pendant입니다.
순수한 가상 기능
이 코드를 사용해보십시오
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()=0;
};
class anotherClass:aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"hellow World";
}
};
int main()
{
//aClassWithPureVirtualFunction virtualObject;
/*
This not possible to create object of a class that contain pure virtual function
*/
anotherClass object;
object.sayHellow();
}
anotherClass 클래스에서 sayHellow 함수를 제거하고 코드를 실행하십시오. 클래스에 순수한 가상 함수가 포함되어 있으면 해당 클래스에서 객체를 만들 수 없으며 상속 된 클래스에서 해당 함수를 구현해야합니다.
가상 기능
다른 코드를 사용해보십시오
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()
{
cout<<"from base\n";
}
};
class anotherClass:public aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"from derived \n";
}
};
int main()
{
aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
baseObject->sayHellow();///call base one
baseObject=new anotherClass;
baseObject->sayHellow();////call the derived one!
}
여기에서 sayHellow 함수는 기본 클래스에서 가상으로 표시됩니다. 파생 클래스에서 함수를 검색하고 함수를 구현하려고 시도하는 컴파일러를 말합니다. 찾을 수 없으면 기본 함수를 실행하십시오.
"가상 함수 또는 가상 메소드는 동일한 서명을 가진 함수로 상속 클래스 내에서 동작을 대체 할 수있는 함수 또는 메소드"-Wikipedia
이것은 가상 기능에 대한 좋은 설명이 아닙니다. 멤버가 가상이 아니더라도 상속 클래스가이를 대체 할 수 있기 때문입니다. 직접 시도해 볼 수 있습니다.
함수가 기본 클래스를 매개 변수로 사용하는 경우 차이점이 표시됩니다. 상속 클래스를 입력으로 제공하면 해당 함수는 재정의 함수의 기본 클래스 구현을 사용합니다. 그러나 해당 기능이 가상 인 경우 파생 클래스에서 구현 된 기능을 사용합니다.
가상 함수는 기본 클래스와 파생 클래스에도 정의가 있어야하지만 필수는 아닙니다. 예를 들어 ToString () 또는 toString () 함수는 가상이므로 사용자 정의 클래스에서 재정 의하여 고유 한 구현을 제공 할 수 있습니다.
가상 함수는 일반 클래스에서 선언되고 정의됩니다.
순수 가상 함수는 "= 0"으로 끝나는 것으로 선언해야하며 추상 클래스에서만 선언 할 수 있습니다.
순수한 가상 함수를 갖는 추상 클래스는 순수한 가상 함수의 정의를 가질 수 없으므로, 해당 추상 클래스에서 파생 된 클래스에서 구현이 제공되어야 함을 의미합니다.