상속과 생성자
파생 클래스의 객체 생성시 생성자와 소멸자 호출 순서
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
{
}
열공 열공!
'C++' 카테고리의 다른 글
[C++] virtual function (가상 함수), function override(오버라이드) (0) | 2021.08.28 |
---|---|
[C++] upcasting (업캐스팅) (0) | 2021.08.27 |
[C++] 상속 (Inheritance), protected (0) | 2021.08.25 |
[C++] this 포인터 (0) | 2021.08.24 |
[C++] 상수 멤버 함수 const (1) | 2021.08.23 |
댓글