▣ 추상 클래스
● 추상 클래스, 상세 클래스
▷ 추상 클래스, abstract class
- 유사한 성격을 가지는 클래스들의 공통적인 요소를 뽑아 만든 클래스로, 일부 메소드가 구체적으로 구현되어 있지 않아 직접적인 사례가 존재하지 않는 클래스를 말한다.
- 추상 클래스로 객체를 직접 정의할 수 없다.
- 추상 클래스는 그 자체로 사용되지 않고 파생 클래스를 통해 구현되어 사용된다.
- 사용 목적: 정 그룹에 속하는 클래스들(파생 클래스들)이 반드시 갖고 있어야하는 행위를 지정해서 필요한 행위를 잊지 않고 정의하도록 하기 위함
▷ 상세 클래스, concrete class
- 클래스의 모든 요소가 구체적으로 구현되어 직접적인 사례가 존재하는 클래스다.
- 상세 클래스는 객체를 정의할 수 있다.
▷ 예시 - Shapes 클래스
- Shapes에 속하는 클래스인 Triangle, Circle 클래스의 객체는 draw나 move 등의 메소드가 정의되어야 한다는 것을 추상클래스인 Shapes 클래스에서 지정한다.
- Shapes 클래스 자체는 draw와 move 메소드를 정의할 수 없어서 객체를 정의할 수 없다.
- 추상 클래스인 'Shapes'에서 구현되지 않은 상태로 상속받은 draw나 move를 상세 클래스에서 구체적으로 정의한다.
- Circle과 Triangle 클래스의 객체를 정의할 수 있다.
- 기초 클래스 Shapes (추상 클래스)
Shapes |
- lineColor : int |
+ setLineColor(c : int) + draw( ) + move( x, y : double) |
- 파생 클래스 Circle (상세 클래스)
Circle |
- cx, cy : double - radius : double |
+ Circle ( x, y, r : double ) + draw( ) + move ( dx, dy : double) |
- 파생 클래스 Triangle (상세 클래스)
Triangle |
- x1, y1 : double - x2, y2 : double - x3, y3 : double |
+ Triangle ( v[3][2] : double ) + draw( ) + move(dx, dy : double) |
● 추상 클래스 선언
▷ 멤버함수 중에 순수가상함수가 포함된 클래스를 선언한다.
- 순수 가상함수 - 구현 부분이 없는 가상함수
- 순수 가상함수 선언 ( =0 에 주목하세요)
virtual RetType functionName(fParameterList) = 0;
▷ 예시
class AClass { //추상 클래스
public:
virtual void vf() = 0; // 순수가상함수
void f1() const
{ cout << "Abstract" << endl;}
};
● 상세 클래스 선언
▷ 순수가상함수를 포함하지 않는 클래스를 선언한다
- 상속받은 순수 가상함수가 있다면 반드시 재정의해야 한다.
class AClass { //추상 클래스
public:
virtual void vf() = 0; // 순수가상함수
void f1() const
{ cout << "Abstract" << endl;}
};
class CClass : public AClass { // 상세 클래스
public:
void vf() { cout << "순수가상함수" << endl;
void f2() const
{ cout << "concrete class" << endl;}
};
- AClass는 객체를 만들 수 없는 추상클래스고, CClass는 상세클래스로 객체를 만들 수 있다.
- 만약에 CClass에서 순수가상함수인 vf( )를 재정의 안했으면 CClass도 추상클래스가 되어서 객체를 만들 수 없게 된다.
● 예제
▷ Shapes 클래스 명세
- 2차원 도형에 해당하는 원을 나타내는 Circle 클래스와 삼각형을 나타내는 Triangle을 선언한다.
- 이 클래스들은 모두 공통적으로 도형 Shapes이기 때문에 도형을 그리기 위한 선의 색과 도형 내부를 채워 칠하기 위한 색을 속성으로 가지고 있다.
- 도형 객체를 (dx, dy)만큼 이동할 수 있고, 2차원 좌표상에서 원점을 기준으로 도형의 크기를 확대 및 축소할 수 있다.
- ㅇㅇ이라는 객체를 만들어서 속성 객체로는 선의 색과 내부 영역을 색칠하기 위한 색을 표현하고, 그 값을 설정하고 읽을 수 있다.
- 도형 객체를 만들면 현재 속성에 따라서 정의된다.
- 도형 객체의 선 색과 채우기 색은 항상 변경할 수 있다.
▷ 그래픽 속성 클래스 - Graphic 클래스
메소드 | 설명 |
Graphic( ) | 생성자 |
void setLineColor(const string&) | 지정된 색으로 선 색 지정 |
void setFillColor(const string&) | 지정된 색으로 내부 영역 색 지정 |
string getLineColor( ) | 선 색 반환 |
string getFillColor( ) | 내부 영역 색 반환 |
속성 | 설명 |
string lineColor | 선 색 속성 |
string fillColor | 내부 영역 색 속성 |
class Graphic {
string lineColor; //선 색 속성
string fillColor; //내부 영역 색 속성
public:
//그래픽 속성 객체 생성자
Graphic() : lineColor("black"), fillColor("white") {}
Graphic(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 Graphic curAttrib; //현재속성을 나타내는 전역 객체
▷ 도형 클래스 - Shape 클래스
메소드 | 설명 |
Shape( ) | 생성자. 현재 속성에 따라 도형 생성 |
void setLineColor(const string&) | 지정된 색으로 선 색 지정 |
void setFillColor(const string&) | 지정된 색으로 내부 영역 색 지정 |
void move(double dx, double dy) | (순수가상함수) 도형 중심 (dx, dy)만큼 이동 |
void scale(double s) | (순수가상함수) 도형의 크기 원점 기준 s배 |
void draw( ) | (순수가상함수) 도형 그리기 |
속성 | 설명 |
Graphic attrib | 도형의 그래픽 속성 |
class Shape {
protected:
Graphic attrib; //그래픽 속성
public:
// 현재 그래픽 속성에 따라 도형 객체 생성
Shape() : 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(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
/* Circle.h */
...
class Circle : public Shape {
double cx, cy; //원의 중심 좌표
double radius; //원의 반지름
public:
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
#include <iostream>
#include "Circle.h"
using namespace std;
// 원 중심 좌표 (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 << "만큼 떨어진 모든 점을 ";
cout << attrib.getLineColor() << "으로 그리고" << endl;
cout << "안은 " << attrib.getFillColor();
cout << " 색으로 채운다." << endl;
}
▷ 삼각형 클래스 - 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
...
class Triangle : public Figure {
double x1, y1, x2, y2, x3, y3; //삼각형 세 꼭지점
double radius;
public:
Triangle(const double v[3][2]);
//그래픽 속성에 따라 삼각형 객체 생성
// v: 3개의 꼭지점 좌표 배열
void move(double dx, double dy); //삼각형 이동
void scale(double s); //원점 기준 크기 조정
void draw() const; // 그리기
};
- Triangle.cpp
#include "Triangle.h"
...
//생성자 - 3 꼭지점 좌표 초기화
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)만큼 3 꼭지점 이동
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;
}
▷ 실행 - Main.cpp
#include <iostream>
#include <string>
#include "Graphic.h"
#include "Shape.h"
#include "Circle.h"
#include "Triangle.h"
using namespace std;
Graphic curAttrib("black", "white"); //현재 그래픽 속성
void drawFigs(const Shape * const figs[], int n)
{
for (int i=0; i < n; i++){
figs[i]->draw();
cout << endl;
}
}
int main()
{
Shape *figs[2];
figs[0] = new Circle(0, 20, 10);
double v[3][2] = { {0,0}, {20,0}, {10,15} };
curAttrib.setLineColor("red");
curAttrib.setFillColor("yello");
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개 이상의 기초 클래스로부터 상속 받는 것을 말한다.
Student |
- school : string |
+ Student ( s : string) + printSchool( ) : void |
Employee |
- company : string |
+ Employee ( c: string ) + printCompany ( ) : void |
둘다▼상속
Parttime |
- |
+Parttime( s : string, c : string ) |
▷ 다중상속 예제
- Student.h
#ifndef STUDENT_H_INCLUDED
#define STUDENT_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
- Employee.h
#ifndef EMPLOYEE_H_INCLUDED
#define EMPLOYEE_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
- Parttime.h
Student와 Employee 둘다한테 상속받는다.
#ifndef PARTTIME_H_INCLUDED
#define PARTTIME_H_INCLUDED
#include <string>
#include "Student.h"
#include "Employee.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
- Main.cpp
#include "Parttime.h"
int main()
{
Parttime jimin("Busan High School", "Bighit Corporation");
jimin.printSchool();
jimin.printCompany();
return 0;
}
● 다중상속의 모호성 해결법
▷ 2개 이상의 기초 클래스로부터 같은 이름의 멤버 함수를 상속받은 경우
Student |
- school : string |
+ Student ( s : string) + print( ) : void |
Employee |
- company : string |
+ Employee ( c: string ) + print( ) : void |
두 기초 클래스에서 print( ) 멤버함수의 이름이 동일하게 정의된 경우가 발생할 수 있다.
Parttime jimin("Busan High School", "Bighit Corporation");
jimin.print(); //오류! 모호성!
▷ 사용하고 싶은 함수의 클래스를 명확하게 명시해준다.
Parttime jimin("Busan High School", "Bighit Corporation");
jimin.Student::print(); //모호성 해결!
● 다중상속에서 공통 기초 클래스 중복 상속
▷ 1개의 기초 클래스가 여러번 상속된다.
- 가장 기초 클래스인 Person에서 받은 name이 Student 클래스와 Employee 클래스를 통해서 Parttime 클래스로 넘어오기 때문에 name 데이터멤버가 2개가 존재하는 상태로 객체가 생성될 수 있다.
Person |
- name : string |
+ Person ( n : string) + print( ) : void |
상▼속
Student | Employee |
- school : string | - company : string |
+ Student ( s : string) + print( ) : void |
+ Employee ( c: string ) +print( ) : void |
상▼속
Parttime |
- |
+Parttime( s : string, c : string ) |
- 가상 기초 클래스로 상속하면 중복 상속을 방지할 수 있다.
▷ 예제
- Person.h
#ifndef PERSON_H_INCLUDED
#define PERSON_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Person {
string name;
public:
Person(const string &n) : name(n) {}
void print() const { cout << name; }
};
#endif
- Student.h
가상 기초 클래스를 virtual 로 지정해서 상속을 받는다.
#ifndef STUDENT_H_INCLUDED
#define STUDENT_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
//가상 기초 클래스로 Person을 상속받음
class Student : virtual public Person {
string school;
public:
Student(const string& n, const string& s) : Person(n), school(s) {}
void print() const {
Person::printc();
cout << " goes to " << school << endl; }
};
#endif
- Employee.h
가상 기초 클래스를 virtual 로 지정해서 상속을 받는다.
#ifndef EMPLOYEE_H_INCLUDED
#define EMPLOYEE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Employee : virtual public Person {
string company;
public:
Employee(const string& n, const string& c) : Person(n), company(c) {}
void printCompany() const {
Person::print();
cout << " is employed by " << company << endl; }
};
#endif
- Parttime.h
가상기초클래스로부터 상속받을 때는 생성자에 가상기초클래스의 생성자를 명시적으로 꼭 호출해야 한다.
멤버 함수가 중복되지 않는 클래스가 생성된다.
#ifndef PARTTIME_H_INCLUDED
#define PARTTIME_H_INCLUDED
#include <string>
#include "Student.h"
#include "Employee.h"
using namespace std;
// Student와 Employee 둘다한테 상속받음
class Parttime : public Student, public Employee {
public:
Parttime(const string& n, const string& s, const string& c)
: Person(n), Student(n, s), Employee(n, c) {} //Person의 생성자를 명시적으로 꼭 호출해야 한다.
void print() const {
Student::print();
Employee::print();
}
};
#endif
'Programming > C++' 카테고리의 다른 글
C++ 언어 기초 (15) - STL, vector, 알고리즘, map (0) | 2020.09.07 |
---|---|
C++ 언어 기초 (14) - 템플릿 template (Feat. 버블정렬) (0) | 2020.09.05 |
C++ 언어 기초 (11) - 상속; 기초/파생클래스, 접근 제어, final, name binding (0) | 2020.09.03 |
C++ 언어 기초 (10) - 연산자 다중정의 II (0) | 2020.09.03 |
C++ 언어 기초 (9) - 연산자 다중정의 (0) | 2020.08.31 |