학습 개요
- 추상 클래스와 다중 상속에 대하여 학습함
- 클래스를 선언할 때 그 클래스의 객체를 직접 만들기 위한 것이 아니라, 파생 클래스에 상속할 공통인 속성이나 메소드를 담고 있게 할 수 있음
- 특히 파생 클래스에서 반드시 가지고 있어야 할 메소드에 대해 구현 부분이 없이 선언만을 포함하도록 선언한 클래스를 추상 클래스라고 함
- 이를 통해 이 클래스의 파생 클래스라면 공통적으로 갖추게 되는 행위를 지정할 수 있어 파생 클래스의 표준화하는 효과를 얻을 수 있음
- 다중 상속은 여러 개의 클래스로부터 상속을 받을 수 있게 하는 기법임
- 그 결과 트리 형태의 클래스 계층이 아닌 그래프 형태의 클래스 계층이 만들어질 수 있음
- 다중 상속을 활용하면서, 이 과정에서 발생할 수 있는 중복 상속의 문제를 해결하기 위한 방법에 대하여 학습함
학습 목표
- 추상 클래스의 목적을 설명할 수 있음
- 순수 가상 함수를 통해 추상 클래스를 선언할 수 있음
- 추상 클래스에서 파생 된 상세 클래스를 선언할 수 있음
- 2개 이상의 기초 클래스로부터 상속을 받는 파생 클래스를 선언할 수 있음
- 다중 상속 결과 발생할 수 있는 기초 클래스 멤버의 중복 상속을 방지할 수 있음
주요 용어
- 추상 클래스(abstract class)
- 유사한 성격을 갖는 클래스들의 공통적 요소들을 뽑아서 만든 클래스로, 일부 메소드의 구체적 구현이 없어 직접적인 사례가 존재하지 않는 클래스
- 상세 클래스(concrete class)
- 클래스의 모든 요소가 구체적으로 구현되어 직접적인 사례가 존재하는 클래스
- 순수 가상 함수 (pure virtual function)
- 행위의 구현 부분이 없이 선언 된 가상 함수
- 다중 상속(multiple inheritance)
- 2개 이상의 기초 클래스로부터 상속을 받는 것
- 공통 기초 클래스
- 다중 상속 과정에서 동시에 상속 받은 2개 이상의 부모 클래스가 공통적으로 상속을 받은 기초 클래스
강의록
추상 클래스
추상 클래스와 상세 클래스
추상 클래스
- 추상 클래스
- 유사한 성격을 갖는 클래스들의 공통적 요소를 뽑아서 만든 클래스로, 일부 메서드의 구체적 구현이 없어 직접적인 사례가 존재하지 않는 클래스
- 추상 클래스로 객체를 직접 정의할 수 없음
- 추상 클래스는 그 자체로 사용되는 것이 아니라 파생 클래스를 통해 구현되어 사용됨
- 사용 목적
- 특정 그룹에 속하는 클래스들(추상 클래스의 파생 클래스들)이 반드시 가지고 있어야 할 행위를 지정함으로써 필요한 행위를 정의하는 것을 누락하지 않도록 함
- ex) 도형 클래스
- 도형에 속하는 클래스(삼각형, 원 등)의 객체는
draw
및move
등의 메서드가 정의되어 있어야 함을 지정함 - 도형 클래스 자체는
draw
및move
등의 메서드를 정의할 수 없어 객체를 만들 수 없음
- 도형에 속하는 클래스(삼각형, 원 등)의 객체는
- 상세 클래스
- 클래스의 모든 요소가 구체적으로 구현되어 직접적인 사례가 존재하는 클래스
- 상속 클래스는 객체를 정의할 수 있음
- ex) 삼각형 및 원 클래스
- 추상 클래스인 ‘도형’에서 구현되지 않은 상태로 상속 받은
draw
,move
등을 구체적으로 정의할 수 있음- 삼각형이나 원 클래스의 객체를 정의할 수 있음
- 추상 클래스인 ‘도형’에서 구현되지 않은 상태로 상속 받은
- 멤버 함수 중 순수 가상 함수가 포함된 클래스를 선언
- 순수 가상 함수
- 구현 부분이 없는 가상 함수
순수 가상 함수의 선언
1
virtual RetType functionName(fParameterList) = 0;
1 2 3 4 5 6
class AClass { // 추상 클래스 public: virtual void vf() const = 0; // 순수 가상 함수 void f1() const { cout << "Abstract" << endl; } };
1
AClass objA; // Error - 추상 클래스의 객체 정의 불가
- 순수 가상 함수
상세 클래스의 선언
- 순수 가상 함수를 포함하지 않는 클래스를 선언
상속 받은 순수 가상 함수가 있다면 반드시 재 정의 해야 함
1 2 3 4 5 6 7 8 9 10 11 12
class AClass { // 추상 클래스 public: virtual void vf() const = 0; // 순수 가상 함수 }; class CClass : public AClass { // 상세 클래스 public: void vf() const { cout << "순수 가상 함수 구현" << endl; } void f2() const { cout << "Concrete" << endl; } };
1
CClass objC; // OK - CClass는 상속 클래스
예제: 추상 클래스의 활용
- 도형 클래스
- 2차원 도형에 해당하는 원을 나타내는 클래스와 삼각형을 나타내는 클래스를 선언하고자 한다.
- 이 클래스들은 모두 공통적으로 도형이므로 도형을 그리기 위한 선의 색과 도형 내부를 채워 칠하기 위한 색을 속성으로 가지고 있어야 하며, 이러한 속성을 이용하여 그리는 방법을 설명할 수 있어야 한다.
- 또한 그래픽 객체를 (dx, dy)만큼 이동할 수 있으며, 2차원 좌표 원점을 기준으로 확대/축소하는 크기 조정을 할 수 있다.
- 프로그램은 현재 속성이라는 객체가 있다.
- 속성 객체는 선의 색과 내부 영역을 칠하기 위한 색을 표현하며, 그 값을 설정하거나 얻어낼 수 있다.
- 도형 객체를 만들면 현재 속성에 따라 만들어진다.
- 또한 도형 객체의 선 색 및 채우기 색을 변경할 수 있다.
예제: 추상 클래스의 활용 - GrAttrib 클래스
그래픽 속성 클래스 GrAttrib의 메서드 및 속성
메서드 비고 GrAttrib()
생성자 void setLineColor(const string&)
지정 된 색으로 선 색 지정 void setFillColor(const string&)
지정 된 색으로 내부 영역 색 지정 string getLineColor()
선 색 반환 string getFillColor()
내부 영역 색 반환 속성 비고 string lineColor
선 색 속성 string fillColor
내부 영역 색 속성
예제: 추상 클래스의 활용 - GrAttrib.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class GrAttrib { // 그래픽 속성 클래스
string lineColor; // 선 색 속성
string fillColor; // 내부 영역 색 속성
public:
// 그래픽 속성 객체 생성자
GrAttrib() : lineColor("검정색"), fillColor("흰색") {} // 기본 값 lineColor 검정색 fillColor 흰색
GrAttrib(const string& lc, const string& fc) : lineColor(lc), fillColor(fc) {} // 매개변수로 선 색상, 내부 색상 받아서 그 값으로 초기화
// 속성 지정 멤버 함수
void setLineColor(const string& lc) { // 선 색상 지정
lineColor = lc;
}
void setFillColor(const string& fc) { // 내부 색상 지정
fillColor = fc;
}
// 속성 값을 얻는 멤버 함수
string getLineColor() const {
return lineColor;
}
string getFillColor() const {
return fillColor;
}
};
extern GrAttrib curAttrib; // 현재 속성을 나타내는 전역 객체
예제: 추상 클래스의 활용 - Figure 클래스
도형 클래스 Figure의 메서드 및 속성
메서드 비고 Figure()
생성자, 현재 속성에 따라 도형을 생성 void setLineColor(const string&)
지정된 색으로 선 색 지정 void setFillColor(const string&)
지정된 색으로 내부 영역 색 지정 void move(double dx, double dy)
도형을 (dx, dy)만큼 이동 void scale(double s)
도형을 원점 기준으로 s배 크기 조정 void draw()
도형을 그림 속성 비고 GrAttrib attrib
도형의 그래픽 속성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
class Figure { protected: GrAttrib attrib; // 그래픽 속성 public: // 현재 그래픽 속성에 따라 도형 객체 생성 Figure() : attrib(curAttrib) {} // 선 색 속성 지정 void setLineColor(const string& c) { attrib.setLineColor(c); } // 내부 영역의 색 지정 void setFillColor(const string& c) { attrib.setFillColor(c); } // 도형의 이동, 원점 기준 크기 조정, 그리기 멤버 함수 (순수 가상 함수) virtual void move(double dx, double dy) = 0; virtual void scale(double s) = 0; virtual void draw() const = 0; };
예제: 추상 클래스의 활용 - Circle 클래스
원 클래스 Circle의 메서드 및 속성
메서드 비고 Circle(double x, double y, double r)
생성자 void move(double dx, double dy)
원을 (dx, dy)만큼 이동 void scale(double s)
원을 원점 기준으로 s배 크기 조정 void draw()
원을 그림 속성 비고 double cx, cy
원의 중심 좌표 double radius
원의 반경
예제: 추상 클래스의 활용 - Circle.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Circle : public Figure {
double cx, cy; // 원의 중심 좌표
double radius; // 원의 반경
public:
// 현재의 그래픽 속성에 따라 원 객체 생성
// (x, y) : 중심좌표
// r : 반경
Circle(double x, double y, double r)
: cx(x), cy(y), radius(r) {}
// 원의 이동, 원점 기준 크기 조정, 그리기 멤버 함수
void move(double dx, double dy);
void scale(double s);
void draw() const;
};
예제: 추상 클래스의 활용 - Circle.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "Circle.h"
// 원의 중심좌표를 (dx, dy)만큼 이동
void Circle::move(double dx, double dy) {
cx += dx;
cy += dy;
}
// 원점 기준으로 s배 크기 조정
void Circle::scale(double s) {
cx *= s;
cy *= s;
radius *= s;
}
// 원을 그리는 방법 출력
void Circle::draw() const {
cout << "원 그리기" << endl;
cout << "(" << cx << ", " << cy << ")로부터 "; // 중심좌표 출력
cout << "radius " << radius << "만큼 떨어진 모든 점들을 "; // 반지름 출력
cout << attrib.getLineColor() << "으로 그리고" << endl; // 선 색상 출력
cout << "내부를 " << attrib.getFillColor(); // 내부 색상 출력
cout << "으로 채운다." << endl;
}
예제: 추상 클래스의 활용 - Triangle 클래스
삼각형 클래스 Triangle 메서드 및 속성
메서드 비고 Triangle(double v[3][2])
생성자 void move(double dx, double dy)
삼각형을 (dx, dy)만큼 이동 void scale(double s)
삼각형을 원점 기준으로 s배 크기 조정 void draw()
삼각형을 그림 속성 비고 double x1, y1
삼각형의 꼭짓점 좌표 double x2, y2
삼각형의 꼭짓점 좌표 double x3, y3
삼각형의 꼭짓점 좌표
예제: 추상 클래스의 활용 - Triangle.h
1
2
3
4
5
6
7
8
9
10
11
12
13
class Triangle : public Figure {
// 삼각형의 세 꼭짓점 좌표 (x1, y1), (x2, y2), (x3, y3)
double x1, y1, x2, y2, x3, y3;
public:
// 현재의 그래픽 속성에 따라 삼각형 객체 생성
// v : 세 개의 꼭짓점 좌표 배열
Triangle(const double v[3][2]);
// 삼각형의 이동, 원점 기준 크기 조정, 그리기 멤버 함수
void move(double dx, double dy);
void scale(double s);
void draw() const;
};
예제: 추상 클래스의 활용 - Triangle.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "Triangle.h"
// 생성자: 세 꼭짓점 좌표를 초기화
Triangle::Triangle(const double v[3][2]) {
x1 = v[0][0]; y1 = v[0][1];
x2 = v[1][0]; y2 = v[1][1];
x3 = v[2][0]; y3 = v[2][1];
}
// 세 꼭짓점 좌표를 (dx, dy)만큼 이동
void Triangle::move(double dx, double dy) {
x1 += dx; y1 += dy;
x2 += dx; y2 += dy;
x3 += dx; y3 += dy;
}
// 세 꼭짓점 좌표를 s배 만큼 조정
void Triangle::scale(double s) {
x1 *= s; y1 *= s;
x2 *= s; y2 *= s;
x3 *= s; y3 *= s;
}
// 삼각형을 그리는 방법 출력
void Triangle::draw() const {
cout << "삼각형 그리기" << endl;
cout << "(" << x1 << ", " << y1 << "), ";
cout << "(" << x2 << ", " << y2 << "), ";
cout << "(" << x3 << ", " << y3 << ")의 좌표를 잇는 선분을 ";
cout << attrib.getLineColor() << "으로 그리고" << endl;
cout << "내부를 " << attrib.getFillColor();
cout << "으로 채운다." << endl;
}
예제: 추상 클래스의 활용 - FigMain.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <string>
#include "GrAttrib.h"
#include "Figure.h"
#include "Circle.h"
#include "Triangle.h"
using namespace std;
// 현재의 그래픽 속성
GrAttrib curAttrib("검정색", "흰색");
void drawFigs(const Figure * const figs[], int n)
{
for (int i = 0; i < n; i++) {
figs[i]->draw(); // 각 도형의 draw() 실행
}
cout << endl;
}
int main()
{
Figure *figs[2];
figs[0] = new Circle(0, 20, 10); // 중심(0,20), 반지름 10인 원 생성
double v[3][2] = { {0, 0}, {20, 0}, {10, 15} }; // 삼각형 꼭짓점 배열
curAttrib.setLineColor("빨강");
curAttrib.setFillColor("노랑");
figs[1] = new Triangle(v);
drawFigs(figs, 2); // 모든 도형 그리기 방법 출력
figs[0]->scale(2); // 첫번쨰 원의 크기 조정
figs[1]->move(5, 10); // 두번째 삼각형의 이동
drawFigs(figs, 2); // 모든 도형 그리기 방법 출력
return 0;
}
다중 상속
다중 상속의 개념
- 다중 상속(multiple inheritance)
- 2개 이상의 기초 클래스로부터 상속을 받는 것
다중 상속의 예 - MIStudent.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef MISTUDENT_H_INCLUDED
#define MISTUDENT_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Student {
string school; // 학교 이름 저장 변수
public:
Student(const string& s) : school(s) {} // 생성자에서 학교명 초기화
void printSchool() const { cout << school << endl; } // 학교명 출력 함수
};
#endif // MISTUDENT_H_INCLUDED
다중 상속의 예 - MIEmployee.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef MIEMPLOYEE_H_INCLUDED
#define MIEMPLOYEE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Employee {
string company; // 회사 이름 저장 변수
public:
Employee(const string& c) : company(c) {} // 생성자에서 회사명 초기화
void printCompany() const { cout << company << endl; } // 회사명 출력 함수
};
#endif // MIEMPLOYEE_H_INCLUDED
다중 상속의 예 - MIParttime.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef MIPARTTIME_H_INCLUDED
#define MIPARTTIME_H_INCLUDED
#include <string>
#include "MIStudent.h"
#include "MIEmployee.h"
using namespace std;
// Student와 Employee를 모두 상속받음
class Parttime : public Student, public Employee {
public:
Parttime(const string& s, const string& c) // 생성자: 학교명, 회사명 전달
: Student(s), Employee(c) {} // 부모 클래스 생성자 호출
};
#endif // MIPARTTIME_H_INCLUDED
다중 상속의 예 - MIMain.cpp
1
2
3
4
5
6
7
8
9
10
#include "MIParttime.h"
int main()
{
Parttime chulsoo("ABC Univ.", "DEF Co."); // 학교, 회사 이름으로 객체 생성
chulsoo.printSchool(); // Student의 멤버 함수 호출
chulsoo.printCompany(); // Employee의 멤버 함수 호출
return 0;
}
다중 상속에서 모호성의 해결
2개 이상의 기초 클래스로부터 동일한 이름의 멤버를 상속 받은 경우
다중 상속에서 공통 기초 클래스의 중복 상속
가상 기초 클래스 - VBPerson.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef VBPERSON_H_INCLUDED
#define VBPERSON_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Person {
string name;
public:
Person(const string &n) : name(n) {}
virtual ~Person() {} // 가상 소멸자(상속 대비)
virtual void print() const { cout << name; } // 이름 출력함수(가상 함수)
};
#endif // VBPERSON_H_INCLUDED
가상 기초 클래스 - VBStudent.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "VBPerson.h"
using namespace std;
// virtual 이용해 Person을 가상 기초 클래스로 상속
class Student : virtual public Person {
string school;
public:
Student(const string& n, const string& s) // 생성자: 이름, 학교명 전달
: Person(n), school(s) {}
void print() const { // print 함수 오버라이드
Person::print(); // 이름 출력(부모 함수 호출)
cout << " goes to " << school << endl;
}
};
가상 기초 클래스 - VBEmployee.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "VBPerson.h"
using namespace std;
// virtual 이용해 Person을 가상 기초 클래스로 상속
class Employee : virtual public Person {
string company;
public:
Employee(const string& n, const string& c)
: Person(n), company(c) {}
void print() const {
Person::print();
cout << " is employed by " << company << endl;
}
};
VBParttime.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "VBStudent.h"
#include "VBEmployee.h"
// Student와 Employee를 상속
class Parttime : public Student, public Employee {
public:
// Person의 생성자를 명시적으로 호출함
Parttime(const string& n, const string& s, const string& c)
: Person(n), Student(n, s), Employee(n, c) {} // Person(n)에서만 Person 생성자 호출이 가능
void print() const {
Student::print();
Employee::print();
}
};
가상 기초 클래스 - VBMain.cpp
1
2
3
4
5
6
7
8
9
10
#include "VBParttime.h"
int main()
{
Parttime chulsoo("Chulsoo", "ABC Univ.", "DEF Co.");
chulsoo.print();
return 0;
}
// Chulsoo goes to ABC Univ. Chulsoo is employed by DEF Co.
연습 문제
추상 클래스와 상세 클래스에 대한 위 지문의 문장 중 올바른 것을 모두 나열한 것은?
1 2 3 4
㈀ 추상 클래스의 객체를 직접 정의할 수 있다. ㈁ 순수 가상함수를 포함하고 있는 클래스는 추상 클래스이다 ㈂ 상세 클래스는 객체를 직접 정의할 수 있다. ㈃ 추상 클래스의 파생 클래스 선언문 내에 순수 가상함수가 포함되어 있지 않다면 그 파생 클래스는 상세 클래스이다.
a. ㈁ 순수 가상 함수를 포함하고 있는 클래스는 추상 클래스이다. ㈂ 상세 클래스는 객체를 직접 정의할 수 있다.
- ㈀ 추상 클래스는 일부 메소드의 구체적 구현이 없어(순수 가상 함수) 객체를 만들 수 없음
- ㈃ 파생 클래스 내에서 순수 가상 함수를 선언하지 않은 것만으로 상세 클래스가 되는 것은 아니고, 기초 클래스에서 상속 받은 모든 순수 가상 함수를 재 정의함으로써, 상속 받은 멤버를 포함한 모든 멤버 함수 중 순수 가상 함수가 없어야 상세 클래스가 됨
위 지문의 클래스가 추상 클래스가 되게 하려고 한다. ㈀에 넣을 멤버 함수 선언문으로 올바른 것은?
1 2 3 4 5 6
class Figure { int lineColor; public: void setLineColor(int c) { lineColor = c; } // _______㈀_______ };
a.
virtual void draw( ) = 0;
- 키워드
virtual
을 사용하여 가상 함수임을 알리고, = 0을 사용하여 순수 가상 함수임을 알림
- 키워드
위 지문은 다중 상속의 예이다. 이 경우 A가 두 번 중복 상속 되는 것을 방지하기 위해 ㈀에 넣을 키워드는?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class A { // 클래스 선언 부분 생략 }; class B : // _______㈀_______ public A { // 클래스 선언 부분 생략 }; class C :// _______㈀_______ public A { // 클래스 선언 부분 생략 }; class D : public B, public C { // 클래스 선언 부분 생략 };
a.
virtual
- 가상 기초 클래스로 상속을 받도록 하기 위해
virtual
이라는 키워드를 사용함
- 가상 기초 클래스로 상속을 받도록 하기 위해
정리 하기
- 추상 클래스는 그 클래스에서는 구체적으로 정의할 수는 없으나 파생 클래스에서 구체적으로 정의해야 하도록 강제하는 순수 가상 함수를 포함함
- 추상 클래스는 객체를 직접 정의할 수 없음
- 추상 클래스의 순수 가상 함수를 모두 재 정의한 파생 클래스는 상세 클래스임
- 다중 상속이란 2개 이상의 기초 클래스로부터 상속을 받는 것임
- 다중 상속의 결과 공통 기초 클래스가 발생할 경우 가상 기초 클래스로 상속 받아 중복 상속을 방지함