728x90
황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!
https://search.shopping.naver.com/book/catalog/32436115747
순수 가상 함수
- 기본 클래스에 작성된 가상 함수는 실행할 목적보다는, 파생 클래스에서 재정의하여 구현할 함수를 알려주는 인터페이스 역할
- 파생 클레스에서 가상 함수를 재정의 하면, Shape 클래스의 draw() 함수처럼, 동적 바인딩에 의해 기본 클래스의 가상 함수는 거의 실행되지 않는다.
- 순수 가상 함수(pure virtual function)는 함수의 코드가 없고, 선언만 있는 가상 함수를 일컫는다.
- 멤버 함수의 원형 뒤에 =0;으로 선언하며, Shape 클래스의 draw()를 순수 가상 함수로 선언하면 아래와 같다.
class Shape {
public:
virtual void draw()=0;
};
추상 클래스
- 최소 하나의 순수 가상 함수를 가진 클래스를 추상 클래스(abstract class)라고 말한다.
class Shape {
public:
void paint() {
draw(); // 순수 가상 함수를 호출할 수 있다.
}
virtual void draw() = 0; // 순수 가상 함수
};
- 추상 클래스는 실행 코드가 없는 순수 가상 함수를 가지고 있기 때문에 불완전한 클래스
- 따라서, 응용프로그램에서 추상 클래스의 인스턴스(객체)를 생성할 수 없다.
- error C2259: 'Shape' : 추상 클래스를 인스턴스화할 수 없습니다. 라는 에러가 발생한다.
Shape shape; // 컴파일 오류
Shape *p = new Shape(); // 컴파일 오류
- 아래와 같이 추상 클래스에 대한 포인터의 선언은 문제되지 않는다.
Shape *p; // 추상 클래스의 포인터 선언
추상 클래스의 목적
- 추상 클래스는 상속을 위한 기본 클래스로 활용하는 것이 목적
- 순수 가상 함수를 통해 파생 클래스가 구현할 함수의 원형을 보여주는 인터페이스 역할
- 일반적으로 추상 클래스는 여러 멤버 변수와 열어 멤버 함수를 구현하고, 파생 클래스에서 구현해야 할 함수만 순수 가상 함수로 선언한다.
추상 클래스 상속
- 추상 클래스를 상속받는 파생 클래스는 자동으로 추상 클래스
class Shape {
public:
void paint() {
draw(); // 순수 가상 함수를 호출할 수 있다.
}
virtual void draw() = 0; // 순수 가상 함수
};
class Circle : public Shape { // Circle은 추상 클래스
public:
string toString() { return "Circle 객체"; }
};
- 마찬가지로 Circle도 추상 클래스 이므로 다음과 같이 객체를 생성할 수 없다.
Circle circle; // 컴파일 오류. 추상 클래스의 객체를 생성할 수 없다.
Circle *p = new Circle(); // 컴파일 오류. 추상 클래스의 객체를 생성할 수 없다.
추상 클래스의 구현
- 추상 클래스의 구현이란, 파생 클래스에 추상 클래스의 순수 가상 함수의 코드를 작성하는 것
- 파생 클래스가 온전한 클래스가 되려면 상속받은 추상 클래스의 모든 순수 가상 함수를 오버라이딩하여 구현
class Shape {
public:
void paint() {
draw(); // 순수 가상 함수를 호출할 수 있다.
}
virtual void draw() = 0; // 순수 가상 함수
};
class Circle : public Shape {
public:
virtual void draw() { cout << "Circle"; } // 순수 가상 함수 구현
string toString() { return "Circle 객체"; }
};
- Circle은 이제 추상 클래스가 아니므로 객체를 생성할 수 있다.
Circle circle; // 정상 코드
Circle *p = new Circle(); // 정상 코드
- 아래는 추상 클래스 Calculator를 상속받은 GoodCalc 클래스를 구현한 것이다.
#include <iostream>
using namespace std;
class Calculator {
public:
virtual int add(int a, int b) = 0; // 두 정수의 합 리턴
virtual int subtract(int a, int b) = 0; // 두 정수의 차 리턴
virtual double average(int a [], int size) = 0; // 배열 a의 평균 리턴. size는 배열의 크기
};
class GoodCalc : public Calculator { // 추상 클래스 구현
public:
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
double average(int a [], int size) {
double sum = 0;
for(int i=0; i<size; i++)
sum += a[i];
return sum/size;
}
};
int main() {
int a[] = {1,2,3,4,5};
Calculator *p = new GoodCalc();
cout << p->add(2, 3) << endl;
cout << p->subtract(2, 3) << endl;
cout << p->average(a, 5) << endl;
delete p;
}
- 아래와 같은 추상 클래스 Calculator를 상속받는 Adder와 Subtractor 클래스를 구현해보자.
- Adder와 Substractor가 Calculator를 구현하기 위해서는 반드시 calck(int a, int b) 함수를 작성해야 한다.
#include <iostream>
using namespace std;
class Calculator {
void input() {
cout << "정수 2 개를 입력하세요>> ";
cin >> a >> b;
}
protected:
int a, b;
virtual int calc(int a, int b) = 0; // 두 정수의 합 리턴
public:
void run() {
input();
cout << "계산된 값은 " << calc(a, b) << endl;
}
};
class Adder : public Calculator {
protected:
int calc(int a, int b) { // 순수 가상 함수 구현
return a + b;
}
};
class Subtractor : public Calculator {
protected:
int calc(int a, int b) { // 순수 가상 함수 구현
return a - b;
}
};
int main() {
Adder adder;
Subtractor subtractor;
adder.run();
subtractor.run();
}
추상 클래스의 용도
- 추상 클래스를 상속받는 파생 클래스는 목적에 따라 순수 가상 함수를 다양하게 구현
- 즉, 추상 클래스를 이용하면 응용프로그램의 설계와 구현을 분리할 수 있다.
- 또한, 추상 클래스는 계층적 상속 관계를 가진 클래스들의 구조를 만들 때 적합하다.
728x90
'Programming Language > C++' 카테고리의 다른 글
[C++] 다양한 제네릭 함수 만들기 (0) | 2023.12.19 |
---|---|
[C++] 템플릿 (0) | 2023.12.19 |
[C++] 가상 함수와 오버라이딩의 활용 사례 (1) | 2023.12.18 |
[C++] 오버라이딩 (0) | 2023.12.18 |
[C++] 가상 함수와 오버라이딩 (1) | 2023.12.18 |