상속이 필요한이유


프로그램을 코딩할때 , 보통 데이터가 묶여있는 클래스와 그 데이터를 이용하여 기능의 처리를담당하는 클래스를 따로 작성하는데 , 이때 기능의 처리를 담당하는 클래스를 컨트롤 ( control ) 클래스 혹은 핸들러 ( handler ) 클래스라고한다. 이때 데이터가 묶여있는 클래스에 새로운 멤버가 생기는등의 변경사항이 있을때 , 컨트롤클래스의 수정이 불가피해지며 , 그 결과 클래스를 전부 수정해야하는 경우가 생긴다. 이러한 불편을 줄이기위해서 소프트설계에있어서 중요시하는것은 " 요구사항의 변경에 따른 프로그램의 유연성 " , " 기능의 추가에따른 프로그램의 확장성 " 이다. 이를위하여 상속의 개념이 도입되었다.


상속이란


B 클래스가 A 클래스를 상속한다면, B 클래스는 A가 가지고있는 모든 멤버를 물려받게된다. 이때 public 상속이냐 , private 상속이냐 , protect 상속이냐에 따라 멤버의 접근권한이 달라진다.


세가지 형태의 상속


class UnivStudent : public Person // public 상속 : Person의 멤버중 Public보다 접근의 범위가 넓은 멤버는 public으로 변경시켜서 상속 ( 그대로 상속 )

class UnivStudnet : private Person // private 상속 : Person 의 멤버중 private 보다 접근의 범위가 넓은 멤버는 private로 변경시켜서 상속 ( Person의 모든멤버를 Private 접근권한으로 상속 ) 

class UnivStudnet : protected Person // protect 상속 : Person 의 멤버중 protect 보다 접근의 범위가 넓은 멤버는 protceted로 변경시켜서 상속 ( Person의 public멤버를 protected 접근권한으로 상속 ) 

거의 대부분의 상속은 public 상속으로 이루어진다


protected 로 선언된 멤버가 허용하는 접근범위


protected 로 선언된 멤버변수는 이를 상속하는 유도클래스에서 접근이 가능하다. 이 키워드를 이용하면 유도클래스에게만 제한적으로 접근을 허용하여 유용하게 사용될 수 있지만, 기본적으로는 기초 클래스와 이를 상속하는 유도클래스 사이에서도 정보은닉이 지켜지는것이 좋다.


상속을 위한 조건


상속을 위한 기본조건인 IS - A 관계


ex ) 

무선전화기 is a 전화기

노트북컴퓨터 is a 컴퓨터


위와같이 상속관계로 묶고자하는 두 클래스가 IS - A 관계로 표현되지 않는다면 적절한상속의 관계가 아닐확률이 높다.


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


ex)

경찰 has a 총


위와같이 소유의 관계를 상속으로 ( 총클래스를 경찰클래스가 상속 ) 해결할 수도 있지만, 이때 경찰이 곤봉을 든다고 가정했을때 , 클래스의 수정이 어려워진다. 따라서 이런경우는 상속이외의 다른방법으로 해결하는것이 좋다.


위 두 상황을 제외하면 , 상속이 형성될 상황은 없다고봐도 무방하다.


상속의 방법


include<iostream>

include<cstring>

using namespace std;


class Person

{

private :

int age;

char name[50];

public :

Person(int myage , char* myname) : age(myage)

{

strcpy(name,myname);

}

void WhatYourName() const

{

cout << "My name is  : " <<name<<endl;

}

void HowOldAreYou() const

{

cout << My age : " << age << endl;

}

};

class UnivStudent : public Person // Person 클래스를 public 으로 상속

{

private :

char major[50]

public :

UnivStudent(char * myname , int myage, char * mymajor) : Person(myage,myname) // 이니셜라이저의 형태로 Person생성자 호출

{

strcpy(major,mymajor);

}

void WhoAreYou()

{

WhatYourName();

HowOldAreYou();

cout << "My major : "<<major <<endl;

}

};


int main (void)

{

UnivStudent ustd1("LEE",22,"Computer eng."); // UnivStudent클래스와 Person 클래스의 생성자 둘다호출 , 초기화

ustd1.WhoAreYou();

return 0;

}


이때 , 기초클래스인 Person의 private 멤버에는 유도클래스인 UnivStudent 에서도 접근이 제한된다. 이는 접근제한의 기준이 객체가아닌 클래스이기때문이다. 따라서 Person에 정의된 Public 함수를 이용해 Person의 private 멤버에 접근해야한다 . 정보의 은닉은 객체내에서도 진행된다. 


유도클래스의 객체 생성과정


유도클래스의 객체생성에서 기초클래스의 생성자는 100% 호출되며 , 유도클래스의 생성자에서 기초클래스의 생성자 호출을 명시하지않으면 기초클래스의 void 생성자가 호출된다.

-> 기초클래스의 생성자와 유도클래스의 생성자는 반드시 모두 호출된다 -> 클래스의 멤버는 해당 클래스의 생성자를 통해서 초기화해야한다.


생성자의 호출은 기초클래스 -> 유도클래스 순으로 호출되며, 소멸자의 호출은 그 반대인 유도클래스 -> 기초클래스의 순으로 진행된다.

-> 소멸자 또한 기초클래스와 유도클래스 모두 호출된다 -> 만약 생성자에서 동적할당한 멤버가 있다면 , 그 메모리공간은 각 클래스의 소멸자에서 해제해야한다.


ex)


class Person

{

private : 

char * name;

public :

Person ( char * myname);

{

name = new char[strlen(myname)+1]; // 동적할당

strcpy(name,myname);

}

~Person ()

{

delete[] name; // 해당클래스에서 메모리해제

}

};


+ Recent posts