[ 12주차 -1 ]
가상 함수와 다형성
파생형에 대한 포인터
부모 클래스 포인터는 부모 클래스에서 상속된 어떠한 클래스의 객체도 가리킬 수 있지만 자식 클래스에 대한 포인터를 이용하여 부모 클래스형의 객체를 접근할 수 없다.
부모 클래스 포인터를 이용하여 자식 클래스에 의해 정의된 원소들을 접근하려면, 그것을 자식 클래스의 포인터로 형변환 시켜야한다.
즉, 부모 클래스에서 상속받은 객체들은 부모 클래스에 존재하는 포인터로 접근할 수 있지만 자식 클래스에서 새로 생성된 객체들은 부모 클래스에 존재하는 포인터로 바로 접근할 수 없어 자식 클래스를 붙여줘서 자식클래스의 포인터형으로 형변환을 해 접근해야한다.
포인터는 부모 형과 관련하여 증가되거나 감소될 수 있다.
포인터가 부모 클래스의 객체를 가리키고 있다면 부모 클래스의 객체가 차지하는 메모리의 크기만큼 증가/감소한다.
부모 클래스 참조도 자식 형의 객체를 참조하는데 사용할 수 있다.
→ 부모 클래스 참조 매개변수는 부모 클래스의 객체뿐만 아니라 그 부모 클래스에서 상속된 클래스의 객체를 받을 수 있다.
#include <iostream>
#include <cstring>
using namespace std;
class B_class {
private:
char author[80];
public:
void put_author(const char* s) {
strcpy_s(author, s);
}
void show_author() {
cout << author << "\n";
}
};
class D_class : public B_class {
private :
char title[80];
public:
void put_title(const char* num) {
strcpy_s(title, num);
}
void show_title() {
cout << "Title: " << title << "\n";
}
};
int main() {
B_class* p;
B_class B_ob;
D_class* dp;
D_class D_ob;
p = &B_ob; // base의 주소를 넣어준다.
//포인터를 통해 B_class에 접근한다.
p->put_author("Tom Clancy");
//포인터를 통해 D_class에 접근한다.
p = &D_ob; // p에 자식에 대한 주소를 넣어준다.
p->put_author("William Shakespeare");
// 각 저자가 적절한 객체에 저장되었음을 보여준다.
B_ob.show_author();
D_ob.show_author();
cout << "\n";
dp = &D_ob; // dp는 자식 클래스에 대한 포인터
dp->put_title("The Tempest");
p->show_author(); // p또는 dp가 여기서 사용될 수 있다.
dp->show_title(); //((D_class *)p)->show_title();
return 0;
}
//Tom Clancy
//William Shakespeare
//William Shakespeare
//Title: The Tempest
B_class에 대한 포인터는 자식 클래스 D_class의 객체를 가리킬 수 있으며, 부모 클래스로부터 상속된 자식 클래스 원소들을 접근할 수 있다.
put_title()과 show_title()은 부모 클래스의 일부분이 아니기 때문에 이들은 부모 포인터인 p를 통해 접근할 수 없으며, 직접 접근하거나 자식 클래스에 대한 포인터를 통해서 접근해야한다.
자식 클래스에 대한 포인터를 dp에 넣는다. dp는 자식 클래스에 대한 포인터이기 때문에 dp를 통해서는 부모 클래스로 부터 상속받은 멤버 함수와 추가된 멤버 함수에도 접근할 수 있다. 그래서 put_title()은 바로 dp로 접근할 수 있다.
p도 자식 클래스 객체에 대한 주소를 갖고있기 때문에 p를 통해 show_author()를 출력할 수 있다. show_author()는 부모로부터 물려받은 멤버 함수이기 때문에 부모 클래스에 대한 포인터를 가지고 접근할 수 있다. p 대신에 dp를 사용해서 접근할 수도 있다.
show_title()은 자식 클래스에 대한 포인터인 dp를 가지고 접근할 수 있지만 부모 클래스에 대한 포인터 p를 가지고 접근하기 위해서는 그냥 호출하는 것이 아니라 형변환을 해서 접근해야한다.
가상함수 ( virtual function )
가상 함수는 실행 시간 다형성을 실현하기 위한 방법이다. ( C++나 JAVA에 존재 )
부모 클래스에서 virtual로 선언되고 하나 또는 그 이상의 자식 클래스에서 재정의되는 함수이다.
→ 각 자식 클래스는 자신만의 가상 함수 버전을 가질 수 있다.
부모 클래스와 자식클래스가 존재할 때 부모 클래스에 있는 함수를 자식 클래스에서 상속받으면 그 함수는 그대로 사용할 수 있다. 하지만 가상함수의 개념으로는 부모 클래스로 부터 상속받은 함수를 자식 클래스에서 재정의해서 사용한다는 의미가 된다.
가상 함수 선언 : 부모 클래스 내부에서 선언부 앞에 virtual 키워드를 작성
다형성 클래스 ( polymorphic class ) : 가상 함수를 포함하는 클래스( 부모 클래스를 상속받는 클래스에도 적용 )
#include <iostream>
using namespace std;
class base {
public:
virtual void who() { // 가상 함수 명시
cout << "Base\n";
}
};
class first_d : public base {
public :
void who() { // who()를 재정의
cout << "First derivation\n";
}
};
class second_d : public base {
public:
void who() { // who()를 재정의
cout << "Second derivation\n";
}
};
// 재정의 하지 않은 경우는 부모 클래스의 who()를 그대로 사용한다.
int main() {
base base_obj;
base* p;
first_d first_obj;
second_d second_obj;
p = &base_obj;
p->who(); //base의 who를 접근한다.
// 부모 클래스 포인터는 자식 클래스의 객체를 참조할 수 있다.
p = &first_obj;
p->who(); // first_d의 who를 접근한다.
p = &second_obj;
p->who(); // second_d의 who를 접근한다.
return 0;
}
//Base
//First derivation
//Second derivation
who()는 가상으로 선언되기 때문에 C++는 실행 시에 p가 가리키는 객체의 형에 의해 어떤 버전의 who()가 참조되는지를 결정한다.
p가 부모 클래스의 포인터임에 불구하고 자식 클래스에 포함되어있는 who()에 접근할 수 있는 이유는 who()는 가상 함수이지만 부모 클래스로 부터 물려받은 멤버 함수이기 때문에 p를 가지고 접근이 가능하다.
즉 부모 클래스의 포인터는 자식 클래스의 객체를 참조할 수 있는데 단, 부모 클래스로 부터 상속받은 멤버를 부모 클래스의 포인터로 접근할 수 있다.
가상 함수의 특징
1. 매개 변수의 형 또는 수가 반드시 같아야만 한다. ( 만약 프로토타입이 다르다면 그 함수는 단순히 중복된 것으로 생각되며, 가상 함수의 성질을 잃게 된다. )
2. 가상 함수를 정의하는 클래스의 프렌드가 아닌 멤버가 되어야만 한다.
→ 가상함수는 반드시 friend가 될 수 없고 멤버 함수만 될 수 있다.
3. 소멸자 함수가 가상 함수로 되는 것은 허용되나, 생성자 함수가 가상 함수로 되는 것은 허용되지 않는다.
가상 함수의 상속
어떤 함수가 가상으로 선언되면, 많은 레벨의 자식 클래스들이 생성된다 하더라도 역시 가상으로 남는다.
// base로 부터가 아니라 first_d로 부터 상속된다.
class second_d : public first_d {
public:
void who() { // who()를 재정의
cout << "Second derivation\n";
}
};
// who()는 여전히 가상이며, 적절한 버전이 정확하게 선택된다.
가상 함수의 필요성
가상 함수와 자식 클래스로 실행 시간 다형성을 제공한다.
* 다형성 : 하나의 인터페이스, 여러가지 방법들
가상 함수의 사용으로 부모 클래스는 모든 자식 클래스에서 사용될 원형 인터페이스 ( generic interface )를 정의할 수 있게 된다.
가상 함수의 간단한 응용
#include <iostream>
using namespace std;
class figure {
protected:
double x, y;
public :
void set_dim(double i, double j) {
x = i;
y = j;
}
virtual void show_area() {
cout << "No area computation defined";
cout << "for this class\n";
}
};
class triangle : public figure {
public:
void show_area() {
cout << "Triangle with height " << x << " and base " << y;
cout << " has an area of " << x * 0.5 * y << "\n";
}
};
class rectangle : public figure {
public:
void show_area() {
cout << "Rectangle with dimensions " << x << " x " << y;
cout << " has an area of " << x * y << "\n";
}
};
class circle : public figure {
public:
void show_area() {
cout << "Circle with radius " << x;
cout << " has an area of " << 3.14 * x * x << "\n";
}
};
int main() {
figure* p; // 기본 형에 대한 포인터를 생성한다.
triangle t; // 파생 형의 객체를 생성한다.
rectangle s;
circle c;
p = &t;
p->set_dim(10.0, 5.0);
p->show_area();
p = &s;
p->set_dim(10.0, 5.0);
p->show_area();
p = &c;
p->set_dim(9.0 , 0);
p->show_area();
return 0;
}
// Triangle with height 10 and base 5 has an area of 25
// Rectangle with dimensions 10 x 5 has an area of 50
// Circle with radius 9 has an area of 254.34
순수 가상 함수와 추상 클래스
순수 가상 함수 ( pure virtual function ) : 부모 클래스 내에 선언되며, 부모 클래스와 연관된 정의를 갖지 않는 함수
→ 몸체는 존재하지 않고 프로토타입만 존재한다.
* 자식 클래스는 자신의 버전을 정의해야만 한다.
virtual type func-name(parameter-list) = 0;
type : 함수의 반환형
func-name : 함수의 이름
=0 : 순수 가상 함수를 지정
class figure{
protected:
double x,y;
public:
void set_dim(double i, double j=0){
x=i;
y=j;
}
virtual void show_area() = 0; //순수 가상 함수
}
순수 가상 함수를 상속받는 자식 클래스의 경우는 반드시 자식 클래스에서 재정의 해줘야한다.
순수 가상 함수를 포함하고 있는 함수를 추상 클래스라 한다.
추상 클래스( abstract class ) : 하나 이상의 순수 가상 함수를 가지고 있는 클래스
클래스의 객체는 있을 수 없다. → 하나 또는 그 이상의 함수가 정의를 갖고 있지 않기 때문
추상 클래스는 다른 자식 클래스의 부모 클래스로만 사용되어져야 한다.
실습 문제
show_author() 함수를 가상 함수로 선언하고 자식 클래스에서는 show_author()가 author와 title를 함께 출력하도록 만들어라.
'2CHAECHAE 학교생활 > 객체지향프로그래밍(C++)' 카테고리의 다른 글
[ 객체지향프로그래밍(C++) 13주차 ① ] (0) | 2022.06.04 |
---|---|
[ 객체지향프로그래밍(C++) 12주차 ② ] (0) | 2022.05.29 |
[ 객체지향프로그래밍(C++) 11주차 ① ] (0) | 2022.05.16 |
[ 객체지향프로그래밍(C++) 10주차 ② ] (0) | 2022.05.16 |
[ 객체지향프로그래밍(C++) 10주차 ① ] (0) | 2022.05.09 |