728x90

황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!

https://search.shopping.naver.com/book/catalog/32436115747

 

C++ Programming : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 

순수 가상 함수

  • 기본 클래스에 작성된 가상 함수는 실행할 목적보다는, 파생 클래스에서 재정의하여 구현할 함수를 알려주는 인터페이스 역할
  • 파생 클레스에서 가상 함수를 재정의 하면, 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