가상함수
업캐스팅 돌아보기
C++ 컴파일러는 포인터 연산의 가능성 여부를 판단할 때, 포인터의 자료형을 기준으로 판단하지,
실제 가리키는 객체의 자료형을 기준으로 판단하지 않는다
class Base
{
public:
void BaseFunc() { cout << "Base Function" << endl; }
};
class Derived : public Base
{
public:
void DeriveFunc() { cout << "Derived Function" << endl; }
};
int main(void)
{
Base *bptr = new Derived(); // 컴파일 ok
bptr->DerivedFunc(); // 컴파일 error
...
}
----------------------------------------------------------------------
int main(void)
{
Base *bptr = new Derived(); // 컴파일 ok
Derived *dptr = bptr; // 컴파일 error
...
}
-----------------------------------------------------------------------
int main(void)
{
Derived *dptr = new Derived(); // 컴파일 ok
Base *bptr = dptr; // 컴파일 ok
...
}
첫 번째 케이스
- 업캐스팅으로 객체 생성
- 업캐스팅으로 생성된 객체이기 때문에 자식 자원에 접근 불가
두 번째 케이스
- 업캐스팅으로 객체 생성
- 자식 클래스의 포인터로 업캐스팅된 부모 객체의 주소를 전달하려는 명시 없는 다운캐스팅으로 에러
세 번째 케이스
- 일반 객체 생성
- 부모 클래스의 포인터에 자식 객체의 주소를 업캐스팅으로 인해 전달
다음 예제 케이스로 도출되는 결과를 확인해보자.
class First
{
public:
void FirstFunc() { cout << "FirstFunc" << endl; }
};
class Second: public First
{
public:
void SecondFunc() { cout << "SecondFunc" << endl;
};
class Third: public Second
{
public:
void ThirdFunc() { cout << "ThirdFunc" << endl; }
};
int main()
{
Third *tptr = new Third();
Second *sptr = tptr;
First *fptr = sptr;
tptr->FirstFunc(); (유효)
tptr->SecondFunc(); (유효)
tprt->ThirdFunc(); (유효)
sptr->FirstFunc(); (유효)
sptr->SecondFunc(); (유효)
sptr->ThirdFunc(); (에러)
fptr->FirstFunc(); (유효)
fptr->SecondFunc(); (에러)
fptr->ThirdFunc(); (에러)
...
}
함수 오버라이딩과 포인터 형
class First
{
public:
void MyFunc() { cout << "FirstFunc" << endl; }
};
class Second: public First
{
public:
void MyFunc() { cout << "SecondFunc" << endl; }
};
class Third: public Second
{
public:
void MyFunc() { cout << "ThirdFunc" << endl; }
};
int main(void)
{
Third *tptr = new Third();
Second *sptr = tptr;
First *fptr = sptr;
fptr->MyFunc();
sptr->MyFunc();
tptr->MyFunc();
delete tptr;
return 0;
}
------------------------------------------------------
// 결과
FirstFunc
SecondFunc
ThirdFunc
함수를 호출할 때 사용이 된 포인터의 형에 따라서 호출되는 함수가 결정된다.
포인터의 형에 정의된 함수가 호출된다.
(없으면 상위 단계로 올라가서 호출)
가상함수(Virtual Function)
class First
{
public:
virtual void MyFunc( cout << "FirstFunc" << endl;
}
class Second: public First
{
public:
virtual void MyFunc( cout << "SecondFunc" << endl;
}
class Third: public Second
{
public:
virtual void MyFunc( cout << "ThirdFunc" << endl;
}
int main(void)
{
Third *tptr = new Third();
Second *sptr = tptr;
First *fptr = sptr;
fptr->MyFunc();
sptr->MyFunc();
tptr->MyFunc();
delete tptr;
return 0;
}
------------------------------------------------------
// 결과
ThirdFunc
ThirdFunc
ThirdFunc
virtual 키워드를 사용해서 오버라이딩된 함수를 호출하면, 무조건적으로 제일 마지막으로 오버라이딩 된 함수만 호출된다.
또한 부모 클래스의 함수가 virtural 이라면 자식 클래스는 명시하지 않아도 virtural 로 상속받는다
이런 virtual 의 성격을 통해서 이전 챕터의 Employee 에서 사용하지 못해 주석을 달았던 함수의 사용을 재개시킬 수 있다.
class Employee
{
private:
char name[100];
public:
Employee(char *name)
{
strcpy(this->name, name)
}
void ShowYourName() const
{
cout << "name: " << neam << endl;
}
virtual int GetPay() const
{
return 0;
}
virtual void ShowSalaryInfo() const
{}
};
이렇게 virtual 로 해줌으로써 객체들 각각의 해당 메서드의 오버라이딩 된 함수를 호출할 수 있도록 유도해줄 수 있다.
순수 가상함수와 추상 클래스
class Employee
{
private:
char name[100];
public:
Employee(char *name) { ... }
void ShowYourName() const { ... }
virtual int GetPay() const
{
return 0;
}
virtual void ShowSalaryInfo() const
{}
};
Employee 클래스는 실제 객체로 만들어서 사용하는 용도가 아닌, 고용의 형태를 일반화시켜 주기 위해 제작된 클래스이다.
객체로 사용될리 없는 Employee 클래스에서 GetPay() 와 ShowSalaryInfo() 는 직접적으로 사용될리가 없다.
그렇기 때문에 해당 함수에도 특별한 내용을 정의하지 않은채로 존재한다.
이렇게 클래스에서 정의되지 않은 함수를 가리켜 순수 가상함수라고 하며,
하나 이상의 순수 가상함수를 멤버로 두어서 객체 생성이 불가능한 클래스를 가리켜 추상 클래스라고 한다.
이 두 함수를 순수 가상함수로 대체 한다면 다음과 같다
virtual int GetPay() const = 0;
virtual void ShowSalaryInfo() const = 0;
다형성(Polymorphism)
다형성: 동질 이상의 의미
모습은 같으나 형태가 다름
문장은 같은데 결과는 다
class First
{
public:
virtual void SimpleFunc() { cout << "First" << endl; }
};
class Secon: public First
{
public:
virtual void SimpleFunc() { cout << "Second" << endl; }
};
int main(void)
{
First *ptr = new First();
ptr->SimpleFunc(); // 아래에 동일한 문장이 존재
delete ptr;
ptr = nuw Second();
ptr->SimpleFunc(); // 위에 동일한 문장이 존재
delete ptr;
return 0;
}
- 컴파일타임 다형성 (Compile-time Polymorphism)
- 함수 오버로딩 (Function Overloading)
- 연산자 오버로딩 (Operator Overloading)
- 템플릿 (Templates)
- 런타임 다형성 (Runtime Polymorphism)
- 기반 클래스(Base Class)의 함수를 파생 클래스(Derived Class)에서 오버라이딩(재정의)하고,
- 기반 클래스 포인터/참조를 통해 파생 클래스 객체를 제어할 때
- virtual 키워드를 통해 작동
'C++ > 윤성우의 열혈 프로그래밍 C++' 카테고리의 다른 글
윤성우의 열혈 C++ chpt 13. (3) | 2025.07.30 |
---|---|
윤성우의 열혈 C++ chpt 11. (0) | 2025.07.28 |
윤성우의 열형 C++ chpt.10 (2) | 2025.07.25 |
윤성우의 열혈 C++ chpt9. (4) | 2025.07.21 |
윤성우의 열혈 C++ chpt8. (0) | 2025.07.02 |