본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] 상속과 생성자 (파생 클래스 구현 방법 및 호출 순서)

by 심찬 2021. 8. 26.

 

 

상속과 생성자

 

파생 클래스의 객체 생성시 생성자와 소멸자 호출 순서

  1. 기반(base) 클래스 생성자

  2. 파생(Derived) 클래스 생성자

  3. 파생(Derived) 클래스 소멸자

  4. 기반(base) 클래스 소멸자

 

#include <iostream>
using namespace std;

class Base
{
    int data;
public:
    Base()       { cout << "Base()"    << endl; }
    Base(int a)  { cout << "Base(int)" << endl; }
    ~Base()      { cout << "~Base()"   << endl; }
};

class Derived : public Base
{
public:
    Derived()       { cout << "Derived()"    << endl; }
    Derived(int a)  { cout << "Derived(int)" << endl; }
    ~Derived()      { cout << "~Derived()"   << endl; }
};

int main()
{
    Derived d(5);
}

Derived d(5); 를 실행한 경우

기반(base) 클래스 생성자의 디폴트 생성자를 호출한다.

 

 

기반 클래스의 다른 생성자를 호출하려면 명시적으로 호출해야 한다.

#include <iostream>
using namespace std;

class Base
{
    int data;
public:
    Base()       { cout << "Base()"    << endl; }
    Base(int a)  { cout << "Base(int)" << endl; }
    ~Base()      { cout << "~Base()"   << endl; }
};

class Derived : public Base
{
public:
    Derived()       { cout << "Derived()"    << endl; }
    Derived(int a) : Base(a) { cout << "Derived(int)" << endl; }   // ********
    ~Derived()      { cout << "~Derived()"   << endl; }
};

int main()
{
    Derived d;      // (1) Base()    --> Derived()    --> ~Derived()  --> ~Base()
    Derived d(5);   // (2) Base(int) --> Derived(int) --> ~Derived()  --> ~Base()
}

 

Derived d; 를 실행한 경우

Derived d(5); 를 실행한 경우

Derived(int a) : Base(a) {~}

이렇게 Base(a) 를 호출할 수 있도록 코드를 넣어주어야 Base(int)형을 호출한다.

 

 


 

파생 클래스 생성시 기반 클래스 어떻게 수행?

 

Derived 클래스가 생성될 때 기반 클래스 (Base) 가 존재하는 것이 확인되면,

컴파일러에 의해 자동적으로 아래와 같이 : Base() 와  ~Base() 가 생긴다고 볼 수 있다.

사용자가 다른 버전을 호출하도록 명시적으로 만들지 않으면 아래와 같이 기반 클래스가 연결되어 있음을 기억하자.

class Derived : public Base
{
public:
    Derived()      : Base() { /*~~~*/ }
    Derived(int a) : Base() { /*~~~*/ }
    ~Derived()      { /*~~~*/ ~Base(); }
}

 


상속과 접근 지정자

 

 

아래와 같은 코드에서 Dog은 기본 생성자가 없지만 컴파일러가 자동으로 기본 생성자를 만들어 주기 때문에 아무 문제없이 컴파일 된다.

class Animal
{
public:
    Animal() {}
};

class Dog : public Animal
{
};

int main()
{
    Animal a;
    Dog    d;
}

 

위와 동일한 코드에서 Animal() 이 private에 있다고 하면 아래와 같이 에러가 발생한다.

외부에서 private에 접근하지 못하는 이유 때문이기에 당연한 결과다.

class Animal
{
private:
    Animal() {}
};

class Dog : public Animal
{
};

int main()
{
    Animal a;
    Dog    d;
}

main.cpp:13:12: error: ‘Animal::Animal()’ is private within this context

main.cpp:14:12: error: use of deleted function ‘Dog::Dog()’

main.cpp:7:7: error: ‘Animal::Animal()’ is private within this context

 

 

위와 동일한 코드에서 Animal() 이 protected 에 있다고 하면 아래와 같이 에러가 발생한다.

하지만 Animal a; 에서는 아래와 같은 에러가 발생하는데,,,

Dog d; 에서는 에러가 발생하지 않는다. 왜???

class Animal
{
protected:
    Animal() {}
};

class Dog : public Animal
{
};

int main()
{
    Animal a;
    Dog    d;
}

main.cpp:13:12: error: ‘Animal::Animal()’ is protected within this context

 

위와 동일한 코드에서 Dog d; 는 에러가 발생하지 않는데, 그 이유는 컴파일러가 아래 코드와 같이 자동으로 생성이 되기 때문이다.

자동 생성된 Dog() : Animal() {} 은 파생 클래스에서의 접근이기 때문에 main에서 Dog d;를 수행할 때 아무 문제가 없다.

class Animal
{
protected:
    Animal() {}
};

class Dog : public Animal
{
public:
    Dog() : Animal() {}      // 컴파일러에 의해 해당 코드가 자동 생성된다.
};

int main()
{
    //Animal a;   // error
    Dog    d;   // ok
}

 

 


 

파생 클래스 생성자 만들 때 주의사항

 

파생 클래스를 아래와 같이 코드 작성하면 에러가 발생한다. 

#include <string>

class Person
{
    std::string name;
    int age;
public:
    Person( std::string n, int a) : name(n), age(a) {}
};

class Student : public Person
{
    int id;
public:
    Student()      : id(0) {}      // ******** Problem **********
    Student(int i) : id(i) {}      // ******** Problem **********
};

int main()
{
    Student s1;
}

에러가 발생하는 이유는 파생 클래스 (Student)에서 생성자 옆에 기본적으로 Person() 를 호출하는 코드가 컴파일러에 의해 자동으로 들어가기 때문이다. 위의 Student 클래스 코드와 아래 코드는 동일하게 취급된다.

class Student : public Person
{
    int id;
public:
    Student()      : Person(), id(0) {}    // ******** Problem **********
    Student(int i) : Person(), id(i) {}    // ******** Problem **********
};

Student 객체 생성시 Person()을 호출할 수 없다고 에러가 발생하게 된다.

main.cpp:15:26: error: no matching function for call to ‘Person::Person()’

main.cpp:17:26: error: no matching function for call to ‘Person::Person()’

 

 

그래서 아래와 같이 파생 클래스 생성자에서 기반 클래스의 생성자를 명시적으로 호출해야 한다.

#include <string>

class Person
{
    std::string name;
    int age;
public:
    Person( std::string n, int a) : name(n), age(a) {}
};

class Student : public Person
{
    int id;
public:
    Student()      : Person("unknown", 0),  id(0) {}    //***************** OK
    
    Student(std::string n, int a, int i)  
                   : Person(n, a),  id(i) {}            //***************** OK
};

int main()
{
    Student s1;
    Student s2("kim", 20, 100);
}

정상적으로 수행되는 것을 볼 수 있다.

 

선언부(헤더)와 구현부(소스 코드)가 나누어져 있을 때 코딩 작성 방법인 아래와 같다.

 

실제 초기화 리스트는 구현부 (Person.cpp)에 작성해야 한다.

// =============  Person.h  =============

class Person
{
    std::string name;
    int age;
public:
    Person( std::string n, int a) : name(n), age(a) {}
};

// =============  Student.h  =============

#include "Person.h"

class Student : public Person
{
    int id;
public:
    Student();
    Student(std::string n, int a, int i);
};

// =============  Person.cpp  =============

#include "Student.h"

Student::Student() : Person("unknown", 0), id(0)      //*****************  OK
{   
}

Student::Student(std::string n, int a, int i) 
        : Person(n, a), id(i)                         //*****************  OK
{   
}

 

 

열공 열공!

댓글