[C++] 상속 : 기초

Vaughan·2022년 8월 15일
0

C++

목록 보기
5/6
post-thumbnail

1.상속:Ingeritance의 이해

00-객체지향에서 중요시 하는 원리

  1. 요구사항의 변경에 쉽게 대응할 수 있는 프로그램의 유연성
  2. 다른 기능을 쉽게 추가할 수 있는 프로그램의 확장성

01-상속이 필요한 이유

과거에 상속을 바라보았던 관점

기존에 정의해 놓은 클래스의 재활용을 목적으로 만들어진 문법적 요소가 상속이다.

→ 그러나 상속은 재활용을 목적으로만 존재하는 문법적 요소가 아니며, 다양한 이점들이 존재한다.

어떤 회사가 직원의 급여를 관리하기 위해 어떤 프로그램을 만들었다. 회사 초기에는 ‘정규직'으로 직원의 종류가 1가지였지만, 점점 회사가 커지면서 직원의 역할이 세분화됨에 따라 직원의 고용형태가 달라졌다고하자. 이미 설계해둔 프로그램을 이용하여 현재의 조건에 맞는 기능을 할 수 있게 변경할 수는 없을까?

→ 상속을 적용하면 가능하다!


02-상속의 방법

자식클래스가 부모클래스를 상속하게 되면, 자식클래스는 부모클래스가 지니고 있는 모든 멤버를 물려받는다.

  • 자식클래스를 정의할 때, 자식클래스의 이름 뒤에 : 접근제한자 부모클래스이름 을 붙여 상속할 수 있다.
  • 소스코드
    /*부모클래스 Person*/
    class Person {
    		private:
    				int age;
    				char name[50];
    		public:
    				Person(int myage, char* myname) : age(myage) {
    						strcpy(name, myname);
    				}
    				void ShowInfo() const {
    						cout<<"Age: "<<age<<endl;
    						cout<<"Name: "<<name<<endl;
    				}
    };
    /*자식클래스 Student*/
    class Student : public Person {
    		private:
    				char major[50];
    		public:
    				Student(int myage, char* myname, char* mymajor) : Person(myage, myname) {
    						strcpy(major, mymajor);
    				}
    				void ShowInfo_std() const {
    						ShowInfo();
    						cout<<"Major: "<<major<<endl;
    				}
    };
  • 상속받은 클래스의 객체가 가지는 멤버
    • 자식클래스를 정의한 코드만 보면 major라는 멤버 변수와 ShowInfo_std()라는 멤버메소드만 가지고 있는 것 같지만, 실제로는 자기자신의 멤버 + 부모의 멤버(age, name, ShowInfo())를 모두 멤버로 가지고 있다.
    • 따라서 자식클래스안에서 부모클래스의 멤버함수를 호출할 수 있었던 것이다.

03-자식클래스의 생성자 정의

  • 자식클래스의 멤버 초기화
    • 자식클래스의 생성자에서는 부모클래스의 멤버변수까지 모두 초기화 해야할 의무가 있다.
    • 클래스의 멤버를 초기화 할 때는 멤버의 초기화를 목적으로 정의되었던 해당 클래스의 생성자를 호출하여 초기화하는 것이 정석이다. ⇒ 따라서 자식클래스의 생성자는 자신이 상속한 부모클래스의 멤버를 초기화하기 위해 부모클래스의 생성자를 자기자신의 생성자에서 호출한다.
  • 생성자 구성
    • 자식클래스의 생성자에서는 부모클래스의 멤버를 초기화하기위한 인자의 전달까지 요구한다.
    • 이니셜라이저가 의미하는 바는 부모클래스의 생성자의 호출이다.
      Student(int myage, char* myname, char* mymajor) //부모멤버 초기화를 위한 인자요구
      		: Person(myage, myname) //부모생성자의 호출
      		{
      				strcpy(major, mymajor);  //자기자신의 멤버의 초기화
      		}

04-자식클래스의 객체 생성 및 소멸

자식클래스의 객체 생성

  • 자식 클래스 객체 생성과정
    1. 클래스를 생성하기 위해 메모리 공간이 할당된다.

    2. 이후 객체 생성 명령문에 의해 인자가 전달되면서 생성자가 호출된다.

    3. 먼저 부모 클래스의 생성자 호출을 위해 이니셜 라이저를 찾고,

      a. 부모클래스의 생성자가 명시되어있으면 명시된 부모클래스의 생성자를 호출한다.

      b. 명시되어있지 않다면 void 생성자를 호출한다.
      *void 생성자 → 전달받는 인자가 없는 생성자. (default 생성자 or 전달받는 인자없이 초기화하는 생성자)

    4. 부모 클래스의 생성자 호출이 완료되어 부모 클래스의 멤버변수가 먼저 초기화된다.

    5. 부모 클래스의 생성자 호출을 위해 실행되지 않았던 자식 클래스의 생성자 실행이 마저 진행되면서 자식 클래스의 멤버변수도 초기화된다.

  • 자식 클래스 객체 생성 특징

    • 자식 클래스의 객체 생성 과정에서 부모 클래스의 생성자는 무조건 호출된다.
    • 자식 클래스의 생성자에서 만약 부모 클래스의 생성자 호출을 명시하지 않았다면, 부모 클래스의 default 생성자가 호출된다.

      부모 클래스의 생성자 정의에 따른 결과

      • 부모 클래스에서 어떤 생성자도 정의하지 않았을 때

        defalt 생성자가 사용된다. 따라서 생성만 했을 뿐 초기화를 위한 어떤 작업도 해주지 않았기 때문에 부모 클래스의 멤버변수에는 쓰레기 값이 들어있다.

      • 부모 클래스에 void생성자가 아닌 다른 생성자(=인자를 전달받는)를 정의했을 때

        • 자식 클래스의 default 생성자를 사용할 수 없다.
        • 반드시 자식 클래스 뒤에 정의해 둔 부모 클래스의 생성자를 명시해야한다. (명시하지 않으면 에러 발생)

자식클래스의 객체 소멸

  • 자식 클래스의 객체 생성과정에서 생성자가 2번 호출된 것처럼, 자식 클래스의 객체가 소멸할 때는 소멸자가 2번 호출된다.
  • 자식 클래스 객체 소멸 특징
    • 자식 클래스의 객체가 소멸될 때는, 다음 순서를 따른다. (생성자 호출순서와 역순)
      1. 자식 클래스의 소멸자 실행

      2. 부모 클래스의 소멸자 실행

        ⇒ 스택에 생성된 객체의 소멸순서는 생성순서와 반대이다.

    • 따라서 상속과 연관된 클래스에서는 생성자에서 동적할당한 메모리는 소멸자에서 해제하도록 정의해야한다.

+ 용어정리

PersonStudent
상위클래스하위클래스
기초(base)클래스유도(derived)클래스
슈퍼(super)클래스서브(sub)클래스
부모클래스자식클래스

2.상속의 세 가지 형태

01-protected 선언의 접근 범위

private < protected < public

  • privateprotected는 모두 해당 클래스의 외부에서는 접근이 불가능하지만 내부에서는 접근이 가능하다.

  • 그러나 상속에서는 이 2가지 키워드의 차이점을 한눈에 알 수 있다!

    • private : 자식클래스는 부모클래스의 외부이기 때문에 private로 선언된 부모 클래스의 멤버에 접근할 수 없다. → 상속과정에서는 어떤 접근제한범위로 선언된 멤버든 모두 상속된다.(private 멤버도 상속됨) 그러나 접근제한의 기준은 객체가 아니라 클래스이기 때문에, 부모 클래스의 외부인 자식 클래스에서는 private 멤버에 직접접근은 할 수 없다.
    • protected : 그러나 private와 다르게 protected로 선언된 부모 클래스의 멤버는 부모 클래스의 뫼부인 자식 클래스 안에서 접근할 수 있다!

      자식클래스에게만 제한적으로 접근을 허용한다.

  • 소스코드

    /*부모 클래스*/
    class Base {
    		private:
    				int num1;
    		protected:
    				int num2;
    		public:
    				int num3;
    };
    /*자식 클래스*/
    class Derived : public Base {
    		public:
    				void Show(){
    						cout<<"private number"<<num1;     // 컴파일 에러
    						cout<<"protected number"<<num2;   // 접근 가능!
    						cout<<"public number"<<num3;      // 접근 가능!
    				}
    };

02-3가지 형태의 상속

자식 클래스에서 상속을 명시하는데 있어서 3가지의 접근제한자를 모두 사용할 수 있다. [상속의 형태를 명시]

class 자식클래스 :접근제한자부모클래스 {};


03-protected 상속

protected보다 접근의 범위가 넓은 멤버(=public)는 protected로 변경시켜서 상속한다.

→ 따라서 protected 상속을 하게 되면, 기존에 클래스 밖에서 접근이 가능하던 멤버도 밖에서 접근할 수 없도록 보호된다.

  • private 멤버 → 직접 접근 불가능한 멤버
  • protected 멤버 → protected 멤버
  • public 멤버 → public 멤버

04-private 상속

private보다 접근의 범위가 넓은 멤버(=public, protected)는 private로 변경시켜서 상속한다.

→ 따라서 private 상속을 한 클래스를 다시 한번 더 상속하게되면 모든 멤버(변수,함수)가 private이기 때문에, 자식클래스에서는 부모클래스의 모든 멤버에 직접접근할 수 없게된다. (사실상 의미없는 상속)

  • private 멤버 → 직접 접근 불가능한 멤버
  • protected 멤버 → private 멤버
  • public 멤버 → private 멤버

05-public 상속

public보다 접근의 범위가 넓은 멤버(=없음)는 public으로 변경시켜서 상속한다.

⇒ private를 제외한 나머지 멤버는 모두 그냥 그대로 상속한다.

  • private 멤버 → 직접 접근 불가능한 멤버
  • protected 멤버 → protected 멤버
  • public 멤버 → public 멤버

*실제로는 public 이외의 상속은 다중상속과 같이 특별한 케이스가 아니라면 사용하지 않는다.


3.상속을 위한 조건

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

상속관계가 성립하기 위해서는 자식 클래스와 부모클래스 간에 IS-A 관계가 성립해야한다.

자식클래스 is a 부모클래스

  • 스마트폰은 일종의 전화기이다. → smart phone is a phone.
  • 노트북은 일종의 컴퓨터이다. → notebook is a computer

02-HAS-A 관계 (소유의 관계)

자식클래스는 부모 클래스가 가지는 모든 멤버를 소유하기 때문에 소유의 관계도 상속으로 표현할 수 있다.

자식클래스 has a 부모클래스

(단, 소유의 관계는 상속이 아니라 가지고 있는 객체를 생성해서 사용하는 등의 방법을 사용하여 표현할 수 있다. )

  • 경찰이 총을 가지고 있다. → police has a gun
  • 상속관계로 표현하기 힘든 소유 관계
    • 총을 가지고 있지 않은 경찰의 표현
    • 총 만이 아니라 다른 도구도 소유하고 있는 경찰의 표현 → 상속은 묶인 두 클래스는 강한 연관성을 띠기 때문에, 다양한 조건을 가질 수 있는 소유관계는 다른 복합 관계로 대신하는 것이 일반적이다.

본문은 ⟪열혈 C++ 프로그래밍, 윤성우⟫ 도서에 기반하여 정리한 내용입니다.

profile
우주의 아름다움도 다양한 지식을 접하며 스스로의 생각이 짜여나갈 때 불현듯 나를 덮쳐오리라.

0개의 댓글