220209, C++ Ch.07-2

상속 관련 용어 정리

앞전까지 상속과 관련된 클래스들을 설명할 때, "상속을 받은 클래스", "상속을 한 클래스" 이런 식으로 용어를 정리했는데, 계속 이렇게 부를수는 없는 노릇이다. 그러므로, 용어를 정리하고 가겠다.

이렇게. 이 중 C++을 사용하는 사람들 사이에서 가장 일반적으로 사용되는 표현은 기초 클래스 & 유도 클래스라고 하므로, 이를 사용하겠다.

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

말 그대로, 유도 클래스의 객체 생성 과정을 이해해보자.

예제를 보자.

/* example.cpp */

#include <iostream>
using namespace std;

class Base {

private:
    int baseNum;
public:
    Base() : baseNum(20) {
        cout<<"Base() call"<<endl;
    }
    Base(int n) : baseNum(n) {
        cout<<"Base(int n) call"<<endl;
    }
    void ShowBaseData() {
        cout<<baseNum<<endl
    }
};

class Derived : public Base {
// Base를 상속받는다. Derived가 상속하는 클래스, Base가 상속되는 클래스
private :
    int derivNum;
public :
    Derived() : derivNum(34) {
        cout<<"Derived() called"<<endl;
    }
    Derived(int n) : derivNum(n) {
        cout<<"Derived(int n) called"<<endl;
    }
    Derived(int n1, int n2) : Base(n1), derivNum(n2) {
        cout<<"Derived(int n1, int n2) called"<<endl;
    }

    void ShowDerivNum() {
        ShowBaseData();
        cout<<derivNum<<endl;
    }
};

int main() {
    cout<<"case 1 :"<<endl;
    Derived dr1;
    dr1.ShowDerivNum();
    cout<<"case 2 :"<<endl;
    Derived dr2(12);
    dr2.ShowDerivNum();
    cout<<"case 3 :"<<endl;
    Derived dr3(14, 83);
    dr3.ShowDerivNum();

    return 0;
}
실행 결과

case 1 :
Base() call
Derived() called
20
34
case 2 :
Base() call
Derived(int n) called
20
12
case 3 :
Base(int n) call
Derived(int n1, int n2) called
14
83

실행결과와 위의 코드를 대조해 보면 알 수 있는 특징이 있다. 우리는 딱히 부른적도 없는 Base class의 생성자까지 불려져 있다는 것.

여기서 알 수 있는 사실이,

1. 유도 클래스(Derived class)의 객체 생성 과정에서 기초 클래스(Base class)의 생성자가 호출된다.
2. 유도 클래스의 생성자애서 기초 클래스의 생성자 호출을 딱히 명시하지 않았다면, 기초 클래스의 void 생성자( Base() )가 호출된다.

그냥 쉽게 생각하면, 유도 클래스의 선언 후 기초클래스의 생성자 호출이 하나 추가된 것만 생각해주면 된다.

유도 클래스의 객체 소멸 과정

위의 예시에서 소멸자만 한번 추가 해보겠다.

/* example.cpp */

#include <iostream>
using namespace std;

class Base {

private:
    int baseNum;
public:
    Base() : baseNum(20) {
        cout<<"Base() call"<<endl;
    }
    Base(int n) : baseNum(n) {
        cout<<"Base(int n) call"<<endl;
    }
    ~Base() {
        cout<<"~Base() call"<<endl;
    }
    void ShowBaseData() {
        cout<<baseNum<<endl
    }
};

class Derived : public Base {
// Base를 상속받는다. Derived가 상속하는 클래스, Base가 상속되는 클래스
private :
    int derivNum;
public :
    Derived() : derivNum(34) {
        cout<<"Derived() called"<<endl;
    }
    Derived(int n) : derivNum(n) {
        cout<<"Derived(int n) called"<<endl;
    }
    Derived(int n1, int n2) : Base(n1), derivNum(n2) {
        cout<<"Derived(int n1, int n2) called"<<endl;
    }
    ~Derived() {
        cout<<"~Derived() called"<<endl;
    }

    void ShowDerivNum() {
        ShowBaseData();
        cout<<derivNum<<endl;
    }
};

int main() {
    Derived drv1(12);
    Derived drv2(24);
    return 0;
}
실행 결과

Base() call
Derived(int n) called
Base() call
Derived(int n) called
~Derived() called
~Base() called
~Derived() called
~Base() called

여기서 알 수 있는 사실은.

1. 유도 클래스의 객체가 소멸될 때에는, 유도 클래스의 소멸자가 먼저 실행되고 이후 기초 클래스의 소멸자가 실행된다.

만약 유도클래스와 기초클래스가 하나 생성되고 소멸되기까지의 과정을 읊는다면

기초클래스 생성자 호출 >> 유도클래스 생성자 호출 >> 유도클래스 소멸자 호출 >> 기초클래스 소멸자 호출

이런 순서로 진행 될 것이다.

  • 그리고 부가적으로, 만약 생성자에서 new 동적 할당을 진행했다면 소멸자에서 free 메모리 해제를 진행해줘야 한다.

예제는 아래에서 보여주겠다.

#include <iostream>
#include <cstring>
using namespace std;

// 각 클래스마다 소멸자가 추가되었다.
// 각 생성자에 동적할당을 해주며, 이에 따라 소멸자에서 메모리 해제를 해준다.

class Person {
private:
    int age;
    char *name;
public:
    Person(int myage, char* myname) : age(myage) {
        name = new char[strlen(myname)+1];
        strcpy(name, myname);
    }

    ~Person() { // 생성자에서 동적 할당한 메모리 공간은 소멸자에서 해제해주는 과정을 거쳐야 한다.
        delete []name;
    }

    void Name() const {
        cout<<"Name is "<<name<<endl;
    }

    void Age() const {
        cout<<age<<"years old"<<endl;
    }
};

class UnivStudent : public Person {// ": public Person"은 Perseon 클래스의 상속을 의미함. 이 문장은 그 중에서도 public 상속을 의미한다.
private:
    char major[50];
public:
    UnivStudent(char *myname, int myage, char *mymajor) : Person(myage, myname) {
        major = new char[strlen(mymajor) + 1];
        strcpy(major, mymajor);
    }

    ~UnivStudent() { // 생성자에서 동적 할당한 메모리 공간은 소멸자에서 해제해주는 과정을 거쳐야 한다.
        delete []major;
    }

    void WhoAreYou() const {
        Name();
        Age();
        cout<<"Major is : "<<major<<endl<<endl;
    } // 이 Class가 Person class를 상속했기에 Person class의 Name 멤버함수, Age 멤버함수를 사용할 수 있다.
};

int main() {
    UnivStudent std1("Go", 23, "Multimedia Eng.");
    std1.WhoAreYou();
    
    UnivStudent std2("Bae", 23, "Math Edu.");
    std2.WhoAreYou();

    return 0;
}

여기까지.

좋은 웹페이지 즐겨찾기