가상함수(Virtual Function)

sz L·2023년 3월 29일
0

C++

목록 보기
25/40
post-thumbnail

가상함수

  • 기본클래스(상속되지 않은 클래스) 내에서 선언되어 파생클래스에 의해 재정의되는 멤버함수
    • 인터페이스를 자식에게 전달하여 재정의 즉 오버라이딩을 하기 위해 사용됨
      • 재정의를 하지 않으면 오류가 발생하여 반드시 자식클래스에서 재정의를 해야한다
      • 가상함수는 함수내부 구현이 되어있는 인터페이스를 자식에게 전달한다
  • 포인터 혹은 기본클래스에 대한 참조를 사용하여 파생클래스의 객체를 참조하면 해당 객체에 대해 가상함수를 호출하고 파생클래스의 함수를 실행함
  • 실행시간에 함수의 다형성을 구현하는데 사용

사용방법

class First
{
public:
	virtual void MyFunc() { ... }
};

자식 클래스의 void MyFunc() 즉 오버라이딩 된 함수는 모두 가상함수가 된다

규칙
1. 클래스의 public 영역에 선언
2. 가상함수는 static일 수 없으며 다른 클래스의 friend가 될 수 없다
3. 가상함수는 기본 클래스의 포인터 또는 참조를 통해서 접근해야함(실행시간의 다형성 얻기 위함)
4. 가상함수의 프로토타입(반환형과 매개변수)은 기본클래스와 파생클래스에서 동일
5. 클래스는 가상 소멸자를 가질 수 있지만 가상 생성자를 가질 수 없다

다형성이란 '동질이상'을 의미한다.
"모습은 같은데 형태는 다르다" => "문장은 같은데 결과는 다르다

#include <iostream>
using namespace std;

class First
{
public:
	virtual void SimpleFunc() { cout << "First" << endl; }
};

class Second :public First
{
public:
	virtual void SimpleFunc() { cout << "Second" << endl; }
};

int main()
{
	First* ptr = new First();
	ptr->SimpleFunc();
	delete ptr;

	ptr = new Second();
	ptr->SimpleFunc();
	delete ptr;

	return 0;
}


위 main함수에는 ptr->SimpleFunc();라는 문장이 두 번 등장한다

  • ptr은 동일한 포인터 변수인데 실행결과가 다른 이유
    • 포인터 변수 ptr이 참조하는 객체의 자료형이 다르기 때문

  1. 가상함수가 안 사용된 코드
#include <iostream>
using namespace std;

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()
{
	Third* tptr = new Third();
	Second* sptr = tptr;
	First* fptr = sptr;

	fptr->MyFunc();
	sptr->MyFunc();
	tptr->MyFunc();

	delete tptr;

	return 0;
}


  1. 가상함수가 사용된 코드
#include <iostream>
using namespace std;

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()
{
	Third* tptr = new Third();
	Second* sptr = tptr;
	First* fptr = sptr;

	fptr->MyFunc();
	sptr->MyFunc();
	tptr->MyFunc();

	delete tptr;

	return 0;
}


정리하기

#include <iostream>
using namespace std;

class Super {
public:
	virtual void func1() {	cout << "Super::func1()" << endl;	}
	virtual void func2() {	cout << "Super::func2()" << endl;	}
	void func3() {	cout << "Super::func3()" << endl;	}
};

class Sub : public Super
{
public:
	void func1() { cout << "Sub::func1()" << endl; } // virtual 함수를 상속받았기 때문에 자동으로 virtual이 생성됨(선언안해도 생성된다)
	void func2() { cout << "Sub::func2()" << endl; }
	void func3() { cout << "Sub::func3()" << endl; }
	void func4() { cout << "Sub::func4()" << endl; }

};

int main()
{
	Super super;
	Sub sub;

	super.func3();
	super.func2();
	super.func1();
	sub.func4();
	sub.func3();		// Super 클래스의(부모클래스) 메소드 상속
	sub.func2();		// Sub 클래스의 메소드에 가려져서 부모클래스의 메소드는 가려짐
	sub.func1();

	cout << endl;

	Super* ps = new Sub; // 자식객체를 부모포인터로 연결한다.
	ps->func3(); // 포인터로 부모클래스에 접근한다
	ps->func2(); // 포인터 타입(Super)기준으로 접근
	ps->func1();
	// virtual 사용하면 자식클래스 접근 가능 ...

	delete ps;

	return 0;
}

부모클래스인class Super에서 virtual void func1()선언 ... class Sub : public Super에서 void func1() 이지만 부모클래스를 상속받았기 때문에 자동으로 가상함수가 된다


가상소멸자

#include <iostream>
using namespace std;

class First {
private:
	char* strOne;
public:
	First(char* str) {
		strOne = new char[strlen(str) + 1];
	}
	~First() {
		cout << "~First()" << endl;
		delete[]strOne;
	}
};

class Second :public First
{
private:
	char* strTwo;
public:
	Second(char* str1, char* str2) :First(str1)
	{
		strTwo = new char[strlen(str2) + 1];
	}
	~Second()
	{
		cout << "~Second()" << endl;
		delete[] strTwo;
	}
};

int main()
{
	First* ptr = new Second("simple", "complex");
	delete ptr;

	return 0;
}  


-> 자식클래스의 소멸자 호출이 안되고있음

virtual키워드로 해결 가능

#include <iostream>
using namespace std;

class First {
private:
	char* strOne;
public:
	First(char* str) {
		strOne = new char[strlen(str) + 1];
	}
	virtual ~First() {
		cout << "~First()" << endl;
		delete[]strOne;
	}
};

class Second :public First
{
private:
	char* strTwo;
public:
	Second(char* str1, char* str2) :First(str1)
	{
		strTwo = new char[strlen(str2) + 1];
	}
	virtual ~Second()
	{
		cout << "~Second()" << endl;
		delete[] strTwo;
	}
};

int main()
{
	First* ptr = new Second("simple", "complex");
	delete ptr;

	return 0;
}

profile
가랑비는 맞는다 하지만 폭풍은 내 것이야

0개의 댓글