순수 가상 함수 (pure virtual function)
- 함수의 구현부가 없고, 선언부가 =0 으로 끝나는 가상함수
추상 클래스 (abstract class)
- 순수 가상 함수가 한 개 이상 있는 클래스
- 객체를 생성할 수 없다.
- 포인터 변수는 만들 수 있다.
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void Draw() = 0;
};
int main()
{
Shape s; // error
Shape* p;
}
출력 : 추상 클래스의 객체를 생성할 수 없기 때문에 에러가 발생한다.
main.cpp:21:11: error: cannot declare variable ‘s’ to be of abstract type ‘Shape’
21 | Shape s; // error
| ^
main.cpp:13:7: note: because the following virtual functions are pure within ‘Shape’:
13 | class Shape
| ^~~~~
main.cpp:16:22: note: ‘virtual void Shape::Draw()’
16 | virtual void Draw() = 0;
|
추상 클래스로 부터 파생된 클래스
- 기반 클래스의 순수 가상함수의 구현부를 제공하지 않으면 역시 추상 클래스이다.
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void Draw() = 0;
};
class Rect : public Shape
{
public:
virtual void Draw() {} // 구현부
};
int main()
{
//Shape s; // error
Shape* p;
Rect r;
}
추상 클래스 설계 의도
- 파생 클래스에게 특정 멤버 함수를 반드시 만들어야 한다고 지시하는 것.
추상 클래스 예제
1. 모든 도형을 타입으로 설계 한다. => Rect, Circle 클래스 설계
2. 모든 도형의 공통의 기반 클래스가 있다면 모든 도형을 하나의 컨테이너에 묶어서 관리 할 수 있다.
- Shape 클래스 도입
- Vector<Shape*>에 모든 도형 보관
3. 모든 도형의 공통의 특징 (Draw함수) 는 반드시 Shape에 있어야 한다.
- Shape* 를 사용해서 Draw를 호출할 수 있어야 한다.
- 그런데 도형의 추상적인 존재이므로 그릴 수 없다.
4. Draw 를 순수 가상함수로 제공하면
모든 도형의 설계자는 반드시Draw 함수의 구현부를 제공해야 한다.
#include <iostream>
#include <vector>
using namespace std;
class Shape
{
public:
virtual void Draw() = 0;
// 모든 도형들은 반드시 이 함수를 구현부에 정의해야 한다.
// 안전성 향상
};
class Triangle : public Shape
{
// virtual void draw() {}
};
class Rect : public Shape
{
public:
virtual void Draw() { cout << "Rect::Draw" << endl;}
};
class Circle : public Shape
{
public:
virtual void Draw() { cout << "Circle::Draw" << endl;}
};
int main()
{
vector<Shape*> v;
while (1 )
{
int cmd;
cin >> cmd;
if ( cmd == 1 ) v.push_back( new Rect );
else if ( cmd == 2 ) v.push_back( new Circle );
else if ( cmd == 9 )
{
for ( auto p : v ) // p 는 Shape* 타입
p->Draw();
}
}
}
강한 결합 (tightly coupling)
객체와 다른 개체와의 관계가 강하게 연결되어 있는 것. 교체 불가능하고 확장성이 없다.
인터페이스 (Interface)
개방 폐쇄의 법칙 (open close principle)
- 기능 확장 (모듈, 클래스, 함수 추가)에 열려 있고, 수정(기존 코드 수정) 에는 닫혀 있어야 한다는 원칙
- 새로운 카메라 클래스가 추가되도 기존 클래스의 코드를 수정하지 않도록 만들어야 한다.
#include <iostream>
class Camera
{
public:
void take()
{
std::cout << "take picture" << std::endl;
}
};
class HDCamera
{
public:
void take()
{
std::cout << "take picture HD" << std::endl;
}
};
class People
{
public:
void useCamera(Camera* p) { p->take();}
void useCamera(HDCamera* p) { p->take();}
};
int main()
{
People p;
Camera c1;
p.useCamera(&c1);
HDCamera hd;
p.useCamera(&hd);
}
계약에 의한 설계
- 사람과 카메라 제작자 사이에 지켜야 하는 규칙을 먼저 설계
- 규칙은 추상 클래스를 사용하여 설계한다.
규칙
- 모든 카메라는 ICamera 로 부터 파생되어야 한다.
카메라 사용자
- 규칙대로만 사용하면 된다.
모든 카메라 제작자
- 반드시 규칙을 지켜야 한다.
#include <iostream>
// 인터페이스
struct ICamera // struct!!!
{
virtual void take() = 0;
};
class People
{
public:
void useCamera(ICamera* p) { p->take();}
};
class Camera : public ICamera
{
public:
void take()
{
std::cout << "take picture" << std::endl;
}
};
class HDCamera : public ICamera
{
public:
void take()
{
std::cout << "take picture hd" << std::endl;
}
};
int main()
{
People p;
Camera c1;
p.useCamera(&c1);
HDCamera c2;
p.useCamera(&c2);
}
약한 결합 (loosely coupling)
객체와 다른 객체와의 관계가 약하게 연결되어 있는 것 (인터페이스를 사용해서 통신), 교체 가능하고 확장성이 좋다.
'C++' 카테고리의 다른 글
[C++] 상속과 RTTI (0) | 2021.12.30 |
---|---|
[C++] RTTI (Run Time Type Information) (0) | 2021.12.29 |
[C++] virtual function (가상 함수), function override(오버라이드) (0) | 2021.08.28 |
[C++] upcasting (업캐스팅) (0) | 2021.08.27 |
[C++] 상속과 생성자 (파생 클래스 구현 방법 및 호출 순서) (0) | 2021.08.26 |
댓글