Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 생성자는 언제 호출될까? 객체 유형별 호출 시점 완벽 정리

C++에서 생성자(Constructor)가 언제 호출되는지는 객체의 선언 방식에 따라 달라집니다. 객체는 크게 전역(global), 지역(local), 정적 지역(static local), 동적(dynamic) 네 가지 유형으로 나눌 수 있으며, 각 유형마다 생성자가 호출되는 시점이 다릅니다.

1. 전역 객체(Global Object): main() 함수 실행 전 호출

전역 객체의 생성자는 프로그램이 main() 함수에 진입하기 이전에 호출됩니다. 즉, main 함수의 첫 번째 문장이 실행되기도 전에 객체가 이미 초기화되어 있는 것입니다.

예제 코드

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Calling Constructor" << endl;
    }
};

MyClass myObj; // 전역 객체

int main() {
    cout << "Inside Main";
}

실행 결과

Calling Constructor
Inside Main

출력 결과를 보면 "Calling Constructor"가 "Inside Main"보다 먼저 출력된 것을 확인할 수 있습니다. 이는 전역 객체의 생성자가 main 함수 진입 전에 호출되었음을 의미합니다.

2. 지역 객체(Local Non-static Object): 객체 생성 지점에서 호출

정적(static)이 아닌 일반 지역 객체의 경우, 생성자는 프로그램 실행 흐름이 객체가 선언된 지점에 도달했을 때 호출됩니다.

예제 코드

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Calling Constructor" << endl;
    }
};

int main() {
    cout << "Inside Main\n";
    MyClass myObj; // 지역 객체
    cout << "After creating object";
}

실행 결과

Inside Main
Calling Constructor
After creating object

main 함수가 시작된 후, 객체 선언문이 실행되는 순간 생성자가 호출되는 것을 볼 수 있습니다.

3. 정적 지역 객체(Static Local Object): 최초 한 번만 호출

함수 내부에 선언된 static 객체의 생성자는 해당 함수가 처음 실행될 때 단 한 번만 호출됩니다. 이후 같은 함수를 다시 호출하더라도 생성자는 재호출되지 않습니다. 이는 정적 객체가 프로그램 종료 시까지 메모리에 유지되기 때문입니다.

예제 코드

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Calling Constructor" << endl;
    }
};

void func() {
    static MyClass myObj; // 정적 지역 객체
}

int main() {
    cout << "Inside Main\n";
    func();
    cout << "After creating object\n";
    func(); // 두 번째 호출 시에는 생성자가 실행되지 않음
    cout << "After second time";
}

실행 결과

Inside Main
Calling Constructor
After creating object
After second time

func()를 두 번 호출했지만, "Calling Constructor"는 한 번만 출력되었습니다. 두 번째 호출에서는 이미 초기화된 기존 객체가 그대로 사용됩니다.

4. 동적 객체(Dynamic Object): new 연산자 사용 시 호출

new 연산자로 힙(heap) 영역에 객체를 생성할 때도 생성자가 호출됩니다. 단, 포인터 변수만 선언한 시점에서는 생성자가 호출되지 않으며, 실제로 new로 객체를 할당하는 순간에 호출됩니다.

예제 코드

#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Calling Constructor" << endl;
    }
};

int main() {
    cout << "Inside Main\n";
    MyClass *ptr;
    cout << "Declaring pointer\n";
    ptr = new MyClass; // 이 시점에 생성자 호출
    cout << "After creating dynamic object";
    delete ptr; // 사용 후 메모리 해제
}

실행 결과

Inside Main
Declaring pointer
Calling Constructor
After creating dynamic object

포인터를 선언하는 것만으로는 생성자가 호출되지 않고, new MyClass;가 실행되는 시점에 비로소 생성자가 호출됩니다. 참고로 동적으로 생성한 객체는 사용 후 반드시 delete로 메모리를 해제해야 하며, 이때 소멸자가 자동으로 호출됩니다.

정리

객체 유형생성자 호출 시점
전역 객체main() 함수 진입 전
지역 객체 (비정적)객체 선언문이 실행되는 시점
정적 지역 객체함수 최초 실행 시 단 한 번
동적 객체new 연산자로 객체를 생성하는 시점

생성자 호출 시점을 정확히 이해하면 객체의 초기화 순서를 예측할 수 있고, 전역 객체 간의 초기화 의존성 문제나 정적 객체의 초기화 타이밍 관련 버그를 예방하는 데 큰 도움이 됩니다.