14. 다형성과 가상 함수
01_ 가상 함수를 사용한 다형성의 구현 02_ 오버라이딩
Shape 클래스와 자식 클래스들(1)
가상 함수가 없으면 문제가 발생하는 경우를 알아보자.
// 일반적인 '도형'을 상징하는 클래스 class Shape
{
public:
void Move(double x, double y);
void Draw() const;
Shape();
Shape(double x, double y);
protected:
double _x, _y;
};
C++ 프로그래밍 기초 - 3 - jshan@cs.kw.ac.kr
Shape::Shape()
{
_x = _y = 0.0;
}
Shape::Shape(double x, double y) {
_x = x;
_y = y;
}
void Shape::Move(double x, double y) {
_x = x;
_y = y;
}
Shape 클래스와 자식 클래스들(2)
가상 함수가 없으면 문제가 발생하는 경우를 알아보자.
// 사각형을 상징하는 클래스
class Rectangle : public Shape {
public:
void Draw() const;
void Resize(double width, double height);
Rectangle();
Rectangle(double x, double y, double width, double height);
protected:
double _width;
double _height;
};
void Rectangle::Draw() const// 멤버변수는 그대로 사용, 함수를 새롭게 정의 {
cout << "[Rectangle] Position = ( " << _x << ", " << _y << ") "
"Size = ( " << _width << ", " << _height << ")\n";
}
C++ 프로그래밍 기초 - 5 - jshan@cs.kw.ac.kr Rectangle::Rectangle()
{
_width = _height = 100.0f;
}
Rectangle::Rectangle(double x, double y, double width, double heig ht)
: Shape(x, y) {
Resize( width, height);
}
void Rectangle::Resize(double width, double height) {
_width = width;
_height = height;
}
Shape 클래스와 자식 클래스들(3)
가상 함수가 없으면 문제가 발생하는 경우를 알아보자.
// 원을 상징하는 클래스
class Circle : public Shape {
public:
void Draw() const;
void SetRadius(double radius);
Circle();
Circle(double x, double y, double radius);
protected:
double _radius;
};
C++ 프로그래밍 기초 - 7 - jshan@cs.kw.ac.kr Circle::Circle()
{
_radius = 100.0f;
}
Circle::Circle(double x, double y, double radius) : Shape(x, y)
{
SetRadius( radius);
}
void Circle::SetRadius(double radius) {
_radius = radius;
}
Shape 클래스와 자식 클래스들(4)
가상 함수가 없으면 문제가 발생하는 경우를 알아보자.
int main() {
// 도형 객체 생성 및 그리기 Shape s;
s.Move(100, 100);
s.Draw();
// 사각형 객체 생성 및 그리기 Rectangle r;
r.Move( 200, 100);
r.Resize( 50, 50);
r.Draw();
// 원 객체 생성 및 그리기 Circle c;
c.Move( 300, 100);
c.SetRadius( 30);
c.Draw();
return 0;
}
C++ 프로그래밍 기초 - 9 - jshan@cs.kw.ac.kr
Shape 클래스와 자식 클래스들(5)
도형 클래스들의 상속 계층도
[23-3]
다양한 클래스의 객체를 배열에 담기 (1)
도형 클래스의 객체들을 동적 배열에 담아서 사용하는 예
Shape* shapes[5] = {NULL};
shapes[0] = new Circle( 100, 100, 50);
shapes[1] = new Rectangle( 300, 300, 100, 100);
shapes[2] = new Rectangle( 200, 100, 50, 150);
shapes[3] = new Circle(100, 300, 150);
shapes[4] = new Rectangle( 200, 200, 200, 200);
for (int i = 0; i < 5; ++i) shapes[i]->Draw();
for (i = 0; i < 5; ++i) {
delete shapes[i];
shapes[i] = NULL;
C++ 프로그래밍 기초 - 11 - jshan@cs.kw.ac.kr
다양한 클래스의 객체를 배열에 담기 (2)
배열과 객체들의 메모리 구조
[23-5]
가상 함수로 문제점 해결하기
Draw() 함수를 가상 함수로 만들어서 문제를 해결해보자.
실행 결과 – 알맞은 클래스의 Draw() 함수가 호출된다.
class Shape {
public:
void Move(double x, double y);
virtual void Draw() const;
// 자식 클래스에서는 생략 가능 Shape();
Shape(double x, double y);
protected:
double _x, _y;
};
C++ 프로그래밍 기초 - 13 - jshan@cs.kw.ac.kr
실습
다양한 도형 추가
Shape 클래스의 자식 클래스로 삼각형, 타원, 선 클래스를 만들어 보자. 또한 다형성을 사용해서 이 클래스들의 객체를 하나의 배열 에 담아서 사용하는 예제를 만들어 보자
다형성 (Polymorphism)과 가상함수(1)
다른 분야에서의 다형성의 의미
객체지향 프로그래밍에서의 다형성이란 타입에 관계 없이 동일 한 방법으로 다룰 수 있는 능력을 말한다.
예) Circle이나 Rectangle 객체들을 각각의 타입에 상관 없이 Shape 객체처럼 다룰 수 있는 능력
[표 23-1]
C++ 프로그래밍 기초 - 15 - jshan@cs.kw.ac.kr
다형성 (Polymorphism)과 가상함수(2)
다형성은 객체간의 결합(Coupling)을 약하게 만들어서, 객체 간 의 연결을 유연하게 해준다.
[23-7]
// 도형을 원점으로 이동하는 함수
void Controller::MoveToOrigin( Shape* p )
{// Circle 객체와 Rectangle 객체 모두 이 함수의 인자로 전달 가능 p->Move( 0, 0 );
p->Draw();
}
순수 가상 함수 (Pure Virtual Functions)
Shape::Draw() 함수를 순수 가상 함수로 만들어보자.
실행 결과 (이전 버전과 다른 점이 없다.)
class Shape {
public:
void Move(double x, double y);
virtual void Draw() const = 0;//함수에 0 대입
//함수원형만 선언 가능 // 중간 생략
/* 함수의 정의를 지워도 컴파일 오류가 발생하지 않는다.
void Shape::Draw() const {
cout << "[Shape] Position = ( " << _x << ", " <<
_y << ")\n";
}
*/
C++ 프로그래밍 기초 - 17 - jshan@cs.kw.ac.kr
순수 가상 함수의 의미
하나 이상의 순수 가상 함수를 포함하고 있는 클래스를 추상 클 래스(Abstract Class)라고 부른다.
추상 클래스 타입의 의 객체를 생성하는 것은 불가능하고, 오로지 부모 클래스로서만 사용할 수 있다.
Shape::Draw() 함수를 순수 가상 함수로 만드는 것의 효과
Shape 클래스를 추상 클래스로 만들기 때문에 Shape 클래스의 객 체를 만들 수 없다.
컴파일러나 다른 개발자들에게 Shape 클래스는 오로지 부모 클래스로
만 사용할 것이라는 점을 알리는 역할을 한다.
Shape::Draw() 함수를 구현하지 않아도 컴파일 오류가 발생하지 않는다.
컴파일러나 다른 개발자들에게 Shape::Draw() 함수가 자식 클래스에 의해서 오버라이딩 될 것이며, 다형성을 통해서만 호출할 것이라는 점 을 알리는 역할을 한다.
다양한 종류의 멤버 함수
상속과 관련해서 지금까지 살펴본 멤버 함수의 종류
일반적인 멤버 함수
가상 함수
순수 가상 함수
어떤 종류의 멤버 함수를 사용할 지에 대한 가이드 라인
처음엔 그냥 멤버 함수로 만든다.
다형성을 이용해야 하는 경우라면 가상 함수로 만든다.
다형성을 위해서 함수의 원형만 필요한 경우라면 순수 가상 함수로
만든다.
C++ 프로그래밍 기초 - 19 - jshan@cs.kw.ac.kr
오버로딩과 오버라이딩
부모 클래스의 오버로드된 함수 중에서 어느 것 하나라도 오버 라이드 하면 나머지 다른 함수들을 모두 사용할 수 없다.
[23-11]
#include <iostream>
#include <string>
using namespace std;
// 애완동물 클래스 class Pet {
public:
void Eat();
void Eat(const string& it);
string name;
};
void Pet::Eat() {
cout << name << " says, 'Where is the food?'\n";
}
void Pet::Eat(const string& it) {
cout << name << " says, 'I like " << it << ".'\n";
}
// 강아지 클래스
class Dog : public Pet {
public:
void Eat();// Eat() 함수 오버라이드 };
void Dog::Eat() {
cout << name << " says, 'Growl~'\n";
}
int main() {
// 강아지 생성 Dog dog1;
dog1.name = "Patrasche";
// 두 가지 Eat() 함수를 호출한다.
dog1.Eat();
dog1.Eat( "milk" );// dog1.Pet::Eat(“milk”);
return 0;
}
C++ 프로그래밍 기초 - 21 - jshan@cs.kw.ac.kr
1) 업 캐스팅과 다운 캐스팅
형 변환
형 변환
C++ 프로그래밍 기초 - 23 - jshan@cs.kw.ac.kr
1) 업 캐스팅과 다운 캐스팅
형 변환
1) 업 캐스팅과 다운 캐스팅
업 캐스팅
C++ 프로그래밍 기초 - 25 - jshan@cs.kw.ac.kr
[실습하기 12-1] 업캐스팅하기
01 #include<iostream>
02 using namespace std;
03 class Calc{
04 protected:
05 int a;
06 int b;
07 public:
08 Calc();
09 Calc(int new_A,int new_B);
10 void CalcPrn();
11 };
12
13 Calc::Calc() 14 {
15 a=0;
16 b=0;
17 } 18
19 Calc::Calc(int new_A,int new_B) 20 {
[예제 12-1] 12_1.cpp
[실습하기 12-1] 업캐스팅하기
21 a=new_A;
22 b=new_B;
23 } 24
25 void Calc::CalcPrn() 26 {
27 cout<<"--- Calc::CalcPrn ---"<<endl;
28 cout<<a<<"₩t"<<b<<endl;
29 } 30
31 class Add : public Calc{
32 protected:
33 int c;
34 public : 35 Add();
[예제 12-1] 12_1.cpp
C++ 프로그래밍 기초 - 27 - jshan@cs.kw.ac.kr
[실습하기 12-1] 업캐스팅하기
57 void Add::AddPrn() 58 {
59 cout<<"--- Add::AddPrn ---"<<endl;
60 cout<<a<<" + "<<b<<" = "<<c<<endl;
61 } 62
63 void main() 64 {
65 Add AddObj(3, 5);
66 Add *AddPtr;
67 AddPtr= &AddObj;
68 AddPtr->Sum();
69 AddPtr->AddPrn();
70
71 Calc *CalcPtr;
72 CalcPtr = &AddObj; // 업 캐스팅 73 // CalcPtr->Sum();
74 CalcPtr->CalcPrn();
75 }
[예제 12-1] 12_1.cpp
1) 업 캐스팅과 다운 캐스팅
C++ 프로그래밍 기초 - 29 - jshan@cs.kw.ac.kr
[실습하기 12-1] 업캐스팅하기
41 Add::Add() : Calc() 42 {
43 } 44
45 Add::Add(int new_A,int new_B) :Calc(new_A, new_B) 46 {
47 a=new_A;
48 b=new_B;
49 c=0;
50 } 51
52 void Add::Sum() 53 {
54 c=a+b;
55 } 56
[예제 12-1] 12_1.cpp
1) 업 캐스팅과 다운 캐스팅
업 캐스팅 정리
• 업 캐스팅은 파생 객체의 포인터가 기반 객체의 포인터로 형변 환하는 것이다.
• 업 캐스팅을 하면 참조 가능한 영역이 축소된다.
• 업 캐스팅은 컴파일러에 의해서 자동 형변환된다.
C++ 프로그래밍 기초 - 31 - jshan@cs.kw.ac.kr
1) 업 캐스팅과 다운 캐스팅
다운 캐스팅
[실습하기 12-2] 다운 캐스팅하기
62 //12-1.cpp와 중복되는 내용(Calc 클래스와 Add 클래스의 정의)이므로 생략 63 void main()
64 {
65 Calc *CalcPtr;
66 CalcPtr = new Add(3, 5); // 업 캐스팅 67 CalcPtr->CalcPrn();
68 //CalcPtr->Sum();
69
70 Add *AddPtr;
71 AddPtr=(Add *)CalcPtr; // 다운 캐스팅 72 AddPtr->CalcPrn();
73 AddPtr->Sum();
74 AddPtr->AddPrn();
75 }
[예제 12-2] 12_2.cpp
C++ 프로그래밍 기초 - 33 - jshan@cs.kw.ac.kr
[실습하기 12-3] 다운 캐스팅의 잘못된 예 알아보기
// 12-1.cpp와 중복되는 내용(Calc 클래스와 Add 클래스의 정의)이므로 생략 63 void main()
64 {
65 Calc Obj(3, 5);
66 Add *AddPtr ; 67 //AddPtr = &Obj;
68 AddPtr = (Add *)&Obj;
69 AddPtr->CalcPrn();
70 AddPtr->Sum();
71 }
[예제 12-3] 12_3.cpp
1) 업 캐스팅과 다운 캐스팅
C++ 프로그래밍 기초 - 35 - jshan@cs.kw.ac.kr
[실습하기 12-4] 기반 클래스형 포인터 변수로 오버라이딩된 멤버함수 호출 하기
01 #include<iostream>
02 using namespace std;
03 class Calc{
04 protected:
05 int a;
06 int b;
07 public:
08 Calc();
09 Calc(int new_A,int new_B);
10 void Prn(); // 파생 클래스에서 오버라이딩할 멤버함수 11 };
12
13 Calc::Calc() 14 {
15 a=0;
16 b=0;
17 } 18
19 Calc::Calc(int new_A,int new_B) 20 {
[예제 12-4] 12_4.cpp
[실습하기 12-4] 기반 클래스형 포인터 변수로 오버라이딩된 멤버함수 호출 하기
21 a=new_A;
22 b=new_B;
23 } 24
25 void Calc::Prn() // 파생 클래스에서 오버라이딩할 멤버함수 26 {
27 cout<<"--- Calc::Prn ---"<<endl;
28 cout<<a<<"₩t"<<b<<endl;
29 } 30
31 class Add : public Calc{
32 protected:
33 int c;
34 public : 35 Add();
36 Add(int new_A,int new_B);
37 void Sum();
38 void Prn(); // 기반 클래스의 멤버함수를 오버라이딩 39 };
40
[예제 12-4] 12_4.cpp
C++ 프로그래밍 기초 - 37 - jshan@cs.kw.ac.kr
[실습하기 12-4] 기반 클래스형 포인터 변수로 오버라이딩된 멤버함수 호출 하기
41 Add::Add() : Calc() 42 {
43 } 44
45 Add::Add(int new_A,int new_B) :Calc(new_A, new_B) 46 {
47 a=new_A;
48 b=new_B;
49 c=0;
50 } 51
52 void Add::Sum() 53 {
54 c=a+b;
55 } 56
[예제 12-4] 12_4.cpp
[실습하기 12-4] 기반 클래스평 포인터 변수로 오버라이딩된 멤버함수 호출 하기
57 void Add::Prn() // 기반 클래스의 멤버함수를 오버라이딩 58 {
59 cout<<"--- Add::Prn ---"<<endl;
60 cout<<a<<" + "<<b<<" = "<<c<<endl;
61 } 62
63 void main() 64 {
65 Calc *CalcPtr;
66 CalcPtr = new Add(3, 5);
67 CalcPtr->Prn();
68 }
[예제 12-4] 12_4.cpp
C++ 프로그래밍 기초 - 39 - jshan@cs.kw.ac.kr
2) 동적 바인딩과 가상함수
정적 바인딩
컴파일할 때 미리 호출될 함수의 결정하는 것으로 이른(early) 바인 딩 이라고도 함
2) 동적 바인딩과 가상함수
정적 바인딩
C++ 프로그래밍 기초 - 41 - jshan@cs.kw.ac.kr
2) 동적 바인딩과 가상함수
가상 함수로 동적 바인딩
동적 바인딩을 늦은(Lately) 바인딩이라고도 하는데 이는 호출할 함수가 컴파일할 때 결정하지 않고 프로그램이 실행되는 동안에 결정하기 때문이 다.
C++에서 멤버함수가 동적 바인딩하도록 하려면 어떻게 해야 할까?
C++에서는 동적 바인딩을 하기 위해 virtual 예약어를 제공한다.
기반 클래스에 가서 함수의 원형 정의(선언) 앞에 virtual를 붙이면 된다.
이때 virtual 예약어를 붙인 함수를 가상함수라고 한다.
2) 동적 바인딩과 가상함수
C++ 프로그래밍 기초 - 43 - jshan@cs.kw.ac.kr
[실습하기 12-5] 가상함수로 동적 바인딩하기
01 #include<iostream>
02 using namespace std;
03 class Calc{
04 protected:
05 int a;
06 int b;
07 public:
08 Calc();
09 Calc(int new_A,int new_B);
10 virtual void Prn(); // 기반 클래스에서 가상함수 선언 11 };
12
13 Calc::Calc() 14 {
15 a=0;
16 b=0;
17 } 18
19 Calc::Calc(int new_A,int new_B) 20 {
21 a=new_A;
22 b=new_B;
23 } 24
[예제 12-5] 12_5.cpp
[실습하기 12-5] 가상함수로 동적 바인딩하기
25 void Calc::Prn() 26 {
27 cout<<"--- Calc::Prn ---"<<endl;
28 cout<<a<<"₩t"<<b<<endl;
29 } 30
31 class Add : public Calc{
32 protected:
33 int c;
34 public : 35 Add();
36 Add(int new_A,int new_B);
37 void Sum();
38 void Prn();
39 };
40
41 Add::Add() : Calc() 42 {
43 } 44
45 Add::Add(int new_A,int new_B) :Calc(new_A, new_B) 46 {
[예제 12-5] 12_5.cpp
C++ 프로그래밍 기초 - 45 - jshan@cs.kw.ac.kr
[실습하기 12-5] 가상함수로 동적 바인딩하기
47 a=new_A;
48 b=new_B;
49 c=0;
50 } 51
52 void Add::Sum() 53 {
54 c=a+b;
55 } 56
57 void Add::Prn() 58 {
59 cout<<"--- Add::Prn ---"<<endl;
60 cout<<a<<" + "<<b<<" = "<<c<<endl;
61 } 62
63 void main() 64 {
65 Calc *CalcPtr;
66 CalcPtr = new Add(3, 5);
67 //CalcPtr->Sum();
68 CalcPtr->Prn();
69 }
[예제 12-5] 12_5.cpp
2) 동적 바인딩과 가상함수
동적 바인딩을 위한 조건
1. 상속 관계에 있는 클래스에서
2. 오버라이딩된 멤버함수가 존재할 경우
3. 이 멤버함수를 기반 클래스에서 가상함수로 선언
C++ 프로그래밍 기초 - 47 - jshan@cs.kw.ac.kr
추상 클래스와 완전 가상함수 이용해 사각형과 원 클래스 설계하기
01 #include<iostream>
02 using namespace std;
03 class Shape 04 {
05 protected : 06 double area;
07 public:
08 virtual void Draw()=0;
09 virtual double GetSize()=0;
10 };
11
12 class Rect : public Shape{
13 protected : 14 int width;
15 int height;
16 public :
17 Rect(int w=0, int h=0);
18 void Draw();
19 double GetSize();
20 };
21
22 Rect::Rect(int w, int h) 23 {
24 width=w;
25 height=h;
26 } 27
[예제 12-6] 12_6.cpp
[실습하기 12-6] 추상 클래스와 완전 가상함수 이용해 사각형과 원 클래스 설 계하기
28 void Rect::Draw() 29 {
30 cout<<"사각형을 그린다"<<endl;
31 } 32
33 double Rect::GetSize() 34 {
35 area=width*height;
36 return area;
37 } 38
39 class Circle : public Shape{
40 protected : 41 int radius;
42 public :
43 Circle(int r=0);
44 void Draw();
45 double GetSize();
46 };
47
48 Circle::Circle(int r) 49 {
50 radius=r;
51 }
[예제 12-6] 12_6.cpp
C++ 프로그래밍 기초 - 49 - jshan@cs.kw.ac.kr
[실습하기 12-6] 추상 클래스와 완전 가상함수 이용해 사각형과 원 클래스 설 계하기
52 void Circle::Draw() 53 {
54 cout<<"원을 그린다"<<endl;
55 } 56
57 double Circle::GetSize() 58 {
59 area=radius*radius*3.14;
60 return area;
61 } 62
63 void main() 64 {
65 Rect recObj(8, 10);
66 recObj.Draw();
67 cout<<"사각형의 면적 : "<<recObj.GetSize()<<endl;
68
69 Circle cirObj(5);
70 cirObj.Draw();
71 cout<<"원의 면적 : "<<cirObj.GetSize()<<endl;
72 }
[예제 12-6] 12_6.cpp
[실습하기 12-7] 추상 클래스와 다형서 이용하기
// 12_6.cpp와 중복되는 내용이므로 생략 63 void CommonPrn(Shape *ptr)
64 {
65 ptr->Draw();
66 }
67 void main() 68 {
69 //Shape obj;
70
71 CommonPrn(new Rect);
72 CommonPrn(new Circle);
73 }
[예제 12-7] 12_7.cpp
C++ 프로그래밍 기초 - 51 - jshan@cs.kw.ac.kr
[실습하기 12-8] 일반 소멸자 사용시 문제점 알아보기
01 #include<iostream>
02 using namespace std;
03 class Base{
04 public:
05 Base();
06 ~Base();
07 };
08 Base::Base() 09 {
10 cout<<" 기반 클래스의 생성자 "<<endl;
11 }
12 Base::~Base() 13 {
14 cout<<" 기반 클래스의 소멸자"<<endl;
15 }
[예제 12-8] 12_8.cpp
[실습하기 12-8] 일반 소멸자 사용시 문제점 알아보기
16 class Derived : public Base{
17 public : 18 Derived();
19 ~Derived();
20 };
21 Derived::Derived() 22 {
23 cout<<" 파생 클래스의 생성자 "<<endl;
24 }
25 Derived::~Derived() 26 {
27 cout<<" 파생 클래스의 소멸자 "<<endl;
28 } 29
30 void main() 31 {
32 Base *BasePtr = new Derived;
33 delete BasePtr;
34 }
[예제 12-8] 12_8.cpp
C++ 프로그래밍 기초 - 53 - jshan@cs.kw.ac.kr
[실습하기 12-9] 가상 소멸자 사용 예 알아보기
01 #include<iostream>
02 using namespace std;
03 class Base{
04 public:
05 Base();
06 virtual ~Base();
07 };
// 11_15.cpp와 중복되는 내용이므로 생략 [예제 12-9] 12_9.cpp
스택
스택
들어온 시간 순으로 데이터를 쌓아갈 때 가장 위에 즉, 가장 최근에 삽입된 위치에 있는 데이터를 삭 제하거나 아니면 거기에 이어서 새로운 데이터를 삽입할 수 있도록 하는 추상 자료형
후입선출(後入先出)
LIFO(Last-In, First-Out: 라이포 또는 리포)
LCFS(Last-Come, First-Served)
cf. 큐: 선입선출(先入先出), FIFO(First-In, First-Out: 파이포 또는 피포)
C++ 프로그래밍 기초 - 55 - jshan@cs.kw.ac.kr
스택 탑, 푸쉬, 팝
한쪽 끝에서 삽입, 삭제
삽입 삭제 위치를 스택 탑 부근으로 제한 함
구현 자료구조에서 탑이 아닌 다른 곳을 접근할 수 있어도 하지 않기로 약속
멤버 함수
- pop( )
- push(const char& entry)
- empty( ) // 스택이 비웠는지 검사
- size( ) // 현재 스택의 아이템 개수 검사하는 접근자 - top( ) // 탑의 아이템을 가져오는 접근자
- etc.
C++ 프로그래밍 기초 - 57 - jshan@cs.kw.ac.kr