C++에서 상속은 객체 지향 프로그래밍의 중요한 개념으로, 부모 클래스의 속성과 메서드를 자식 클래스가 물려받는 기능을 제공합니다. 그러나 상속 관계에서 생성자와 소멸자의 동작 방식은 다소 복잡할 수 있습니다. 이 글에서는 상속에서의 생성자와 소멸자의 작동 원리, 주의사항, 그리고 예제를 통해 이를 자세히 살펴보겠습니다.
1. 생성자의 동작
1.1 생성자의 호출 순서
상속 관계에서 객체가 생성될 때, 부모 클래스의 생성자가 먼저 호출되고, 그 다음에 자식 클래스의 생성자가 호출됩니다. 이는 부모 클래스의 속성이 자식 클래스의 속성보다 먼저 초기화되어야 하기 때문입니다.
class Parent {
public:
Parent() {
cout << "Parent constructor called" << endl;
}
};
class Child : public Parent {
public:
Child() {
cout << "Child constructor called" << endl;
}
};
int main() {
Child child; // Child 객체 생성
return 0;
}
위의 예제에서 Child
객체가 생성될 때, 먼저 Parent
클래스의 생성자가 호출되고, 그 다음에 Child
클래스의 생성자가 호출됩니다. 출력 결과는 다음과 같습니다:
Parent constructor called
Child constructor called
1.2 생성자 초기화 리스트
부모 클래스의 생성자에 매개변수가 있는 경우, 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출하기 위해 초기화 리스트를 사용할 수 있습니다.
class Parent {
public:
Parent(int x) {
cout << "Parent constructor called with value: " << x << endl;
}
};
class Child : public Parent {
public:
Child(int x) : Parent(x) { // 부모 클래스 생성자 호출
cout << "Child constructor called" << endl;
}
};
int main() {
Child child(10); // Child 객체 생성
return 0;
}
이 경우, Child
클래스의 생성자는 부모 클래스의 생성자를 호출하여 초기화합니다. 출력 결과는 다음과 같습니다:
Parent constructor called with value: 10
Child constructor called
2. 소멸자의 동작
2.1 소멸자의 호출 순서
상속 관계에서 객체가 소멸될 때, 자식 클래스의 소멸자가 먼저 호출되고, 그 다음에 부모 클래스의 소멸자가 호출됩니다. 이는 자식 클래스의 자원이 먼저 해제된 후, 부모 클래스의 자원이 해제되어야 하기 때문입니다.
class Parent {
public:
~Parent() {
cout << "Parent destructor called" << endl;
}
};
class Child : public Parent {
public:
~Child() {
cout << "Child destructor called" << endl;
}
};
int main() {
Child* child = new Child(); // 동적 할당
delete child; // Child 객체 소멸
return 0;
}
위의 예제에서 Child
객체가 소멸될 때, 먼저 Child
클래스의 소멸자가 호출되고, 그 다음에 Parent
클래스의 소멸자가 호출됩니다. 출력 결과는 다음과 같습니다:
Child destructor called
Parent destructor called
2.2 가상 소멸자
부모 클래스의 소멸자가 가상 소멸자로 선언되어 있지 않으면, 부모 클래스의 포인터를 통해 자식 클래스의 객체를 삭제할 때 자식 클래스의 소멸자가 호출되지 않을 수 있습니다. 이를 방지하기 위해 부모 클래스의 소멸자는 가상 소멸자로 선언하는 것이 좋습니다.
class Parent {
public:
virtual ~Parent() { // 가상 소멸자
cout << "Parent destructor called" << endl;
}
};
class Child : public Parent {
public:
~Child() {
cout << "Child destructor called" << endl;
}
};
int main() {
Parent* child = new Child(); // 부모 클래스 포인터로 자식 클래스 객체 생성
delete child; // 자식 클래스 객체 소멸
return 0;
}
이 경우, delete child;
를 호출하면 Child
클래스의 소멸자가 먼저 호출되고, 그 다음에 Parent
클래스의 소멸자가 호출됩니다. 출력 결과는 다음과 같습니다:
Child destructor called
Parent destructor called
3. 주의사항
3.1 생성자와 소멸자의 일관성
부모 클래스의 생성자와 소멸자는 자식 클래스의 생성자와 소멸자와 일관성을 유지해야 합니다. 즉, 부모 클래스의 생성자가 매개변수를 받는 경우, 자식 클래스의 생성자에서 이를 적절히 처리해야 합니다.
3.2 메모리 관리
동적 할당된 객체는 반드시 소멸자를 통해 메모리를 해제해야 합니다. 부모 클래스의 포인터를 사용하여 자식 클래스의 객체를 삭제할 때는 가상 소멸자를 사용하여 자식 클래스의 자원이 올바르게 해제되도록 해야 합니다.
3.3 초기화 순서
부모 클래스의 생성자가 먼저 호출되므로, 자식 클래스의 생성자에서 부모 클래스의 멤버를 사용하기 전에 부모 클래스의 생성자가 완료되었는지 확인해야 합니다. 이는 초기화 순서에 따라 발생할 수 있는 문제를 방지하는 데 중요합니다.
4. 결론
C++의 상속에서 생성자와 소멸자는 객체의 초기화와 자원 해제에 중요한 역할을 합니다. 생성자는 부모 클래스의 속성을 먼저 초기화하고, 소멸자는 자식 클래스의 자원을 먼저 해제한 후 부모 클래스의 자원을 해제합니다. 가상 소멸자를 사용하여 올바른 자원 해제를 보장하는 것이 중요하며, 생성자와 소멸자의 일관성을 유지하는 것이 필요합니다. 다음 포스팅에서는 가상 함수에 대해 다룰 예정입니다.
'C++ 프로그래밍' 카테고리의 다른 글
C++의 순수 가상 클래스: 인터페이스의 정의 (0) | 2025.02.09 |
---|---|
C++의 가상 함수: 다형성의 핵심 (0) | 2025.02.08 |
C++의 메서드 재정의: 객체 지향 프로그래밍의 유연성 (0) | 2025.02.08 |
C++의 상속: 객체 지향 프로그래밍의 핵심 개념 (0) | 2025.02.08 |
C++의 단항 증감 연산자: 사용자 정의 타입의 증감 연산 (0) | 2025.02.08 |
댓글