문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 상속 2 (protected로 선언된 맴버가 허용하는 접근의 범위, 3가지 형태의 상속, 상속을 위한 조건)

벼리네 2021. 3. 4. 09:58
반응형

 [문과 코린이의 IT 기록장] C,C++ - 상속 2 

(protected로 선언된 맴버가 허용하는 접근의 범위,  3가지 형태의 상속, 상속을 위한 조건)


 

 

 1. protected로 선언된 맴버가 허용하는 접근의 범위

- C++의 접근 제어 지시자

(허용범위 좁음) private < protected < public (허용범위 넓음)

 

ex )

class A{

private : // 클래스 내부에서 접근 가능

 int num1;

protected : // 클래스 내부에서 접근 가능

 int num2;

public : 

 int num3;

 void ShowData(){

   cout<<num1<<", "<<num2<<", "<<num3;

 }

};

 

class B : public A{

public:

 void ShowAMember(){

   cout<<num1; // 컴파일 에러 ( private 맴버는 유도 클래스에서 접근이 불가능하다. )

   cout<<num2; // 컴파일 가능 ( portected 맴버는 유도 클래스에서 접근이 가능하다. )

   cout<<num3; // 컴파일 가능

 }

};

 

 

-protected 맴버는, 유도 클래스에서만 제한적으로 접근을 허용한다.

 


 

 

 2. 3가지 형태의 상속

1) public 상속

class B : public A { ... }

- A클래스는, public으로 상속되었다.

- public 상속은, 'public보다 접근의 범위가 넓은 맴버는 public으로 변경시켜서 상속하겠다'라는 뜻이다.

 * 즉, private를 제외한 나머지는 그냥 그대로 상속한다는 뜻이다.


 

2) protected 상속

class B : protected A { ... }

- A클래스는, protected로 상속되었다.

 

class A{
private : 
 int num1;
protected : 
 int num2;
public : 
 int num3;
};

class B : protected A{
    ...

};

- protected 상속은, 'protected보다 접근의 범위가 넓은 맴버는 protected로 변경시켜서 상속하겠다.'는 뜻이다.

- 따라서, 위의 내용에서 portected보다 접근 범위가 넓은 맴버 public 맴버이기 때문에, protected 상속을 한 A클래스는 아래의 형태가 된다.

class B : protected A{
private :
 int num1;
protected :
 int num2;
protected :
 int num3;
};

- 그러나, 위의 코드는 잘못 표현된 부분이 있다. 위 코드처럼, num1이 그대로 private 선언이 된다면, 이 A의 맴버는 B 클래스 내에서 접근이 가능할 수 있다.

- 즉, num1은, 선언이 된 A 클래스 이외의 영역에서 접근이 불가능하므로 아래의 형태로 표현되어야 옳다.

class B : protected A{
접근불가 : 
 int num1;
protected :
 int num2;
protected :
 int num3;
};

 

ex )

 

 

#include <iostream>
using namespace std;

class Base {
private:
 int num1;
protected:
 int num2;
public:
 int num3;

 Base():num1(1),num2(2),num3(3){ } // 생성자
};

class Derived : protected Base {  }; //empty!

int main() {
Derived drv; // drv 객체 생성
cout << drv.num3 << endl; // 컴파일 에러 발생!

// Base 클래스를 protected로 상속했기 때문에, public 맴버변수인 num3는 Derived에서 protected맴버가 된 것이다.  따라서 외부에서 접근이 불가능하다.
return 0;
}

 


3) private 상속

class B : private A { ... }

- A클래스는, private로 상속되었다.

 

class A{
private : 
 int num1;
protected : 
 int num2;
public : 
 int num3;
};

class B : private A{
    ...

};

- private 상속은, 'private보다 접근범위가 넓은 맴버는 private로 변경시켜서 상속하겠다.'는 뜻이다.

- 따라서, num2, num3 모두 Derived 클래스 내에서만 접근이 가능한 맴버로 변경된다.

class B : private A{
접근불가 :
 int num1;
접근불가 : 
 int num2;
접근불가 :  
 int num3;
};

 


 

 

 3. 상속을 위한 조건

1) IS-A 관계의 성립

- 상속의 기본 문법에서 보이듯이, 유도 클래스는 기초 클래스가 지니는 모든 것을 지니고, 거기다 유도클래스만의 추가적인 특성이 더해진다.

 

- IS-A관계는, 'A(유도 클래스)는 B(기초 클래스)이다'라는 것을 이야기한다.

 : 상속관계가 성립하려면, 기초클래스와 유도클래스간에 IS-A관계가 성립해야 한다.

 : 만약 그렇지 않을 경우에는 적절한 상속의 관계가 아닐 경우가 높다.

 

ex )

 

 

#include <iostream>

#include <cstring>

using namespace std;

 

class Computer { // 컴퓨터 클래스 : 모든 컴퓨터의 공통적인 속성을 computer 클래스 하나에 표현

private:

 char owner[50]; // 소유주 변수

public:

 Computer(const char* name) { // 컴퓨터 생성자

   strcpy(owner, name);

 }

 void Calculate() { // 계산 기능 출력 함수

   cout << "요청 내용을 계산합니다." << endl;

 }

};

 

class NotebookComp : public Computer { // 노트북 컴퓨터 클래스 

private:

 int Battery; // 베터리 기능 변수 추가

public:

 NotebookComp(const char * name, int initChag) : Computer(name),Battery(initChag){ } // 노트북 생성자

 void Charging() { Battery += 5; } // 베터리 교환 기능 함수 

 void UseBattery() { Battery -= 1; } // 베터리 사용 기능 함수

 void MovingCal() { // 노트북을 사용할 때마다, 베터리가 소모되는 상황에 대한 함수

   if (GetBatteryInfo() < 1) {

     cout << "충전이 필요합니다." << endl;

     return;

   }

   cout << "이동하면서 ";

   Calculate();

   UseBattery();

 }

 int GetBatteryInfo() { return Battery; } // 객체에 대한, 베터리 반환 함수

};

 

class TableNotebook : public NotebookComp { // 테이블노트북 컴퓨터 클래스

private:

 char regstPenModel[50]; // 펜 등록 변수 추가

public:

 TableNotebook(const char* name, int initChag, const char* pen) : NotebookComp(name, initChag)// 테이블노트북 컴퓨터 생성자

   strcpy(regstPenModel, pen);

 }

 void Write(const char* penInfo) { // 펜 사용(필기) 기능 함수

   if (GetBatteryInfo() < 1) {

     cout << "충전이 필요합니다." << endl;

     return;

   }

   if (strcmp(regstPenModel, penInfo) != 0) {

     cout << "등록된 펜이 아닙니다.";

     return;

   }

   cout << "필기 내용을 처리합니다." << endl;

   UseBattery();

 }

};

 

int main() {

NotebookComp nc("이수종", 5);

TableNotebook tn("정수영", 5, "ISE-241-242");

nc.MovingCal();

tn.Write("ISE-241-242");

return 0;

}

 

 

 


2) HAS-A관계

- HAS-A관계 : '소유'의 관계 (~는 ~를 소유하다)

 : HAS-A관계도 상속의 조건은 되지만, 복잡한 관계로, 이를 대신하는 것이 일반적이다.

 

ex 1 )

#include <iostream>

#include <cstring>

using namespace std;

 

class Gun { // 총 클래스

private:

 int bullet; // 장전된 총알의 수

public:

 Gun(int bnum) : bullet(bnum){ } // 총 생성자

 void Shot() { // 총 발사 기능 함수

   cout << "BBANG!" << endl;

   bullet--;

 }

};

 

class Police : public Gun { // 경찰 클래스 (권총을 소유하는 경찰 표현)

private:

 int handcuffs; // 소유한 수갑의 수

public:

 Police(int bnum,int bcuff): Gun(bnum),handcuffs(bcuff){ } // 경찰 생성자

 void PutHandCuff() { // 수갑 기능 함수

   cout << "SNAP!" << endl;

   handcuffs--;

 }

};

 

int main() {

Police pman(5, 3); // pman 객체 생성 (총알 5, 수갑 3)

pman.Shot();

pman.PutHandCuff();

return 0;

}


- 소유의 관계는 상속이 아닌 다른 방식으로도 가능하다.

 

ex 2 )

#include <iostream>

#include <cstring>

using namespace std;

 

class Gun { // 총 클래스

private:

 int bullet; // 장전된 총알의 수

public:

 Gun(int bnum) : bullet(bnum){ } // 총 생성자

 void Shot() { // 총 발사 기능 함수

   cout << "BBANG!" << endl;

   bullet--;

 }

};

 

class Police { // 경찰 클래스

private:

 int handcuffs; // 소유한 수갑의 수 

 Gun* pistol; // 소유하고 있는 권총

public:

 Police(int bnum, int bcuff) : handcuffs(bcuff) { // 경찰 생성자

   if (bnum > 0) 

     pistol = new Gun(bnum); // Gun 클래스를 상속하는 것이 아닌, 생성자에서 객체를 생성하여 이를 참조함.

   else

     pistol = NULL;

 }

 void PutHandCuff() { // 수갑 기능 함수

   cout << "SNAP!" << endl;

   handcuffs--;

 }

 void Shot()// 총 발사 기능 함수

// Gun 객체를 맴버변수 pistol을 통해 참조하는 구조이기 때문에, 이렇게 별도의 함수 정의 필요

   if (pistol == NULL)

     cout << "Hut BBANG!" << endl;

   else

     pistol->Shot();

 }

 ~Police() { // 경찰 소멸자

   if (pistol != NULL)

   delete pistol;

 }

};

 

int main() {

Police pman1(5, 3); // pman1 객체 생성

pman1.Shot();

pman1.PutHandCuff();

 

Police pman2(0, 3); // pman2 객체 생성 (권총을 소유하지 않은 경찰)

pman2.Shot();

pman2.PutHandCuff();

return 0;

}


[ 코드의 양이 늘었음에도, ex 1)보다 ex 2)의 예제가 더 좋은 모델이다. 그 이유는? ]
a. 권총을 소지하지 않은 경찰을 표현해야 할 경우.
b. 경찰이 수갑뿐만 아니라, 다른 것들도 소유하기 시작했을 경우
- 이와 같이 복잡하고 다양한 코드를 구현해야 할 경우, 상황을 대처하는데 이 방법이 더 좋음.

따라서, IS-A방법에서 상속 관계의 표현에 있어 사용하는데 적절하며, HAS-A 관계에서는 프로그램의 변경에 많은 제약을 줄 수 있다.

* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)

 

반응형