본문으로 바로가기

2021.04.04 - [이론공부/객체지향 프로그래밍] - 객체지향 프로그래밍 공부#3 (클래스용어)


상속

객체지향 프로그래밍에서 상속은 우리에게 흔히 부모와 자식의 관계가 아닌 코드를 재사용하기위한 목적으로 이해를 가져야 한다.

 

조금 이해하기 쉽게 C로 작성된 클래스 2개를 보여주겠다.

 

class Cpoint
{
private:
	int m_iX;
	int m_iY;

public:
	void Assign(int x, int y);
	int Add();
};


void Cpoint::Assign(int x, int y) {
	m_iX = x;
	m_iY = y;
}

int Cpoint::Add() {
	return m_iX + m_iY;
}

위와 같이 Cpoint 클래스를 작성했다.

Cpoint 클래스 안에는 m_iY, m_iX, Assign(), Add()가 존재하고 있다.

 

 

class CCircle
{
private:
	int m_iX;
	int m_iY;
	int m_iRadius;
public:
	void Assign(int x, int y);
	void SetRadius(int r);
	int Add();
	double Area();
};

void CCircle::Assign(int x, int y) {
	m_iX = x;
	m_iY = y;
}

void CCircle::SetRadius(int r) {
	m_iRadius = r;
}

int CCircle::Add() {
	return m_iX + m_iY;
}

double CCircle::Area() {
	return 3.14 * m_iRadius * m_iRadius;
}

위에 자성된 CCircle 이라는 클래스는

m_iX, m_iY, m_iRadius, Assign, SetRadius, Add, Area 가 포함되어 있다.

 

여기서 클래스 두개를 동시에 사용하면 m_iY, m_iX, Assign(), Add()가 중복 된다는 것을 알 수 있다.

이는 같은 코드를 두번 쓰는 비효율적인 상황이며 여기서 상속을 사용하면, 코드 재사용에 용이 할수 있다.

 

 

class CCircle : public Cpoint
{
private:
	int m_iRadius;
public:
	void SetRadius(int r);
	double Area();
};


void CCircle::SetRadius(int r) {
	m_iRadius = r;
}

double CCircle::Area() {
	return 3.14 * m_iRadius * m_iRadius;
}

위에서 사용한거 처럼 : public Cpoint를 이용하여 Cpoint를 상속받아. 중복된 코드는 포함이 되어서 따로 클래스내에서 작성하지 않아도 사용할 수 있다.

 

여기서 Cpoint 는 베이스 클래스, 기반 클래스, 기본 클래스 라고 불리며, 상속을 받는 CCircle을 파생 클래스, 유도된 클래스 라고 한다.

 

 

객체간 관계

상속관계를 가지게된 각각클래스로 만들어진 가각객체는 클래스 처럼 따로 관계가 생기지 않습니다.

객체는 독립적이다.

 

int main(void) {

	Cpoint gildong;
	gildong.Assign(2, 3);
	printf("덧셈결과 : %d\n", gildong.Add());


	CCircle younja;
	younja.SetRadius(10);
	printf("원의 면적 : %f\n", younja.Area());

}

각자 객체는 독립적으로 작동한다.

 

 

Private, Protected, Public

class CCircle : public Cpoint
{
private:
	int m_iRadius;
public:
	void SetRadius(int r);
	double Area();
	void Move(int x, int y);
};

void CCircle::SetRadius(int r) {
	m_iRadius = r;
}

double CCircle::Area() {
	return 3.14 * m_iRadius * m_iRadius;
}

void CCircle::Move(int x, int y) {
	m_iX = m_iX + x;
	m_iY = m_iY + y;
}

 

위에 작성한 CCircle 클래스에 Move 함수를 맴버로 추가하였다.

이러면 m_iX, m_iY에 접근권한에 관련된 오류가 발생한다.

그이유는 m_iX, m_iY는 CCircle이 아닌 Cpoint의 Private 맴버 이기 떄문인데.

이런경우엔 접근 권한이 없어서, CCircle에 맴버는 Cpoint에 있는 m_iX, m_iY에게 접근 할 수 없다.

그렇다고 이를 위해서 public을 사용하게 되면 보안성이 떨어질 것이다.

이를 위해서 아래와 같이 작성하면 된다.

 

class Cpoint
{
protected:
	int m_iX;
	int m_iY;

public:
	void Assign(int x, int y);
	int Add();
};

Cpoint 에 m_iX, m_iY를 protected로 선언하면, 자신의 맴버들 뿐만 아니라 상속받은 클래스도 이 맴버들에게 접근 할수 있는 권한을 줄 수 있다.

 

public : 공개

private : 속한맴버들에게만 공개

protected : 속한맴버들뿐만 아니라 상속된 다른 클래스맴버들에게도 공개

 

 

함수 중복정의(함수오버로딩)

int main(void) {

	Cpoint gildong;
	gildong.Assign(2, 3);
 	gildong.Assign(7);
	printf("덧셈결과 : %d\n", gildong.Add());
}

다른 의미로 함수 오버로딩이라고 하며,

위와 같이 같은 객체 함수에 다른 방식으로 파라미터를 보내거나 할때 사용이 됩니다.

 

class Cpoint
{
protected:
	int m_iX;
	int m_iY;

public:
	void Assign(int x, int y);
	void Assign(int a);
	int Add();
};

void Cpoint::Assign(int a) {
	m_iX = a;
	m_iY = a;
}


void Cpoint::Assign(int x, int y) {
	m_iX = x;
	m_iY = y;
}

int Cpoint::Add() {
	return m_iX + m_iY;
}

 

Cpoint 클래스에 파라미터 한개만 넣은 경우의 함수를 추가하면 됩니다.

이를 함수 재정의, 혹은 함수 오버로딩이라 합니다.

 

함수 재정의 (오버라이딩)

class CCircle : public Cpoint
{
protected:
	int m_iRadius;
public:
	void SetRadius(int r);
	double Area();
	void Move(int x, int y);
};

void CCircle::SetRadius(int r) {
	m_iRadius = r;
}

double CCircle::Area() {
	return 3.14 * m_iRadius * m_iRadius;
}

void CCircle::Move(int x, int y) {
	m_iX = m_iX + x;
	m_iY = m_iY + y;
}


class CEllipse : public CCircle
{
private:
	int m_iRadius2;
public:
	void setRadius2(int r);
	double Area();
};

void CEllipse::setRadius2(int r) {
	m_iRadius2 = r;
}

double CEllipse::Area() {
	return 3.14 * m_iRadius2 * m_iRadius;
}

 

위와 같이 CEllipse 라는 타원의 면적을 구하는 클래스를 만들었을때 CCircle의 원의 면적을 구하는 Area 맴버를 상속 받았는데, 목적을 구하는데는 재사용할 여건이 안되어서 다시 덮어씌우는 과정이 있는걸 알 수 있습니다.

이를 위에서 언급한 함수 오버라이딩이라고 합니다.

 

 

int main(void) {

	Cpoint gildong;
	gildong.Assign(2, 3);
	printf("덧셈결과 : %d\n", gildong.Add());


	CCircle younja;
	younja.SetRadius(10);
	printf("원의 면적 : %f\n", younja.Area());

	CEllipse younha;
	younha.SetRadius(100);
	younha.setRadius2(2);
	printf("원의 면적 : %f\n", younha.Area());

}

결과값

덧셈결과 : 5

원의 면적 : 314

원의 면적 : 628

 

위와 같이 메인 함수에서 각각 객체별로 다른 클래스로 만들어지면 각각의 객체들은 독립적으로 다르게 작동이 된다는 것을 알 수 있다.

 

더보기
#include <stdio.h>



class Cpoint
{
protected:
	int m_iX;
	int m_iY;

public:
	void Assign(int x, int y);
	void Assign(int a);
	int Add();
};

void Cpoint::Assign(int a) {
	m_iX = a;
	m_iY = a;
}


void Cpoint::Assign(int x, int y) {
	m_iX = x;
	m_iY = y;
}

int Cpoint::Add() {
	return m_iX + m_iY;
}




class CCircle : public Cpoint
{
protected:
	int m_iRadius;
public:
	void SetRadius(int r);
	double Area();
	void Move(int x, int y);
};

void CCircle::SetRadius(int r) {
	m_iRadius = r;
}

double CCircle::Area() {
	return 3.14 * m_iRadius * m_iRadius;
}

void CCircle::Move(int x, int y) {
	m_iX = m_iX + x;
	m_iY = m_iY + y;
}


class CEllipse : public CCircle
{
private:
	int m_iRadius2;
public:
	void setRadius2(int r);
	double Area();
};

void CEllipse::setRadius2(int r) {
	m_iRadius2 = r;
}

double CEllipse::Area() {
	return 3.14 * m_iRadius2 * m_iRadius;
}



int main(void) {

	Cpoint gildong;
	gildong.Assign(2, 3);
	printf("덧셈결과 : %d\n", gildong.Add());


	CCircle younja;
	younja.SetRadius(10);
	printf("원의 면적 : %f\n", younja.Area());

	CEllipse younha;
	younha.SetRadius(100);
	younha.setRadius2(2);
	printf("원의 면적 : %f\n", younha.Area());

}

수업도중에 작성한 전체 소스 코드

 


느낀점 : 클래스간 맴버의 수가 클래스 뷰탭에 있는 맴버 뿐이라고 생각하지 말고, 어디선가 상속받고 있을것이란 생각을 가지고 있는 것이 객체지향 프로그램에서는 필요하다. 클래스간에 상속 방법, public, private, protected 등의 맴버 접근제어자를 알 수 있었다. 함수 인자를 넘겨줄때 갯수, 변수타입에 따라 함수를 다르게 처리할 수 있도록 하는 함수오버로딩, 좀더 세부적으로 다르게 기능을 설정하기 위해 사용되는 함수오버라이딩을 이해할 수 있었다
수업이 대부분 실습이라 내 나름대로의 해석으로 글을 작성했다.