1. ( ) 소괄호를 사용한 초기화
new T(args) 형태로 사용하는 전통적인 방식입니다. 2010년 이전부터 사용되던 방식이며, 기본적으로 인수가 있는 생성자를 호출할 때 사용됩니다.
• 사용 예시 : new int(10), new Player("Warrior", 55)
• 빈 초기화 : new int() 처럼 인수를 비워두면 0으로 값이 초기화됩니다.
• 단점 (안정성) : 소괄호 방식은 int i = 3.14;처럼 정밀도 손실이 발생하는 "좁아지는 변환(Narrowing Conversion)" 을 허용합니다. 이는 잠재적인 버그의 원인이 될 수 있습니다.
2. { } 중괄호를 사용한 초기화
new T{args} 형태로 사용하는 모던 C++ (C++11 이후) 방식입니다. 유니폼 초기화라고 불리며, 모든 종류의 초기화(동적 할당, 정적 할당, 멤버 초기화 등) 에 일관되게 사용하도록 설계되었습니다.
• 사용 예시 : new int{10}, new Player{"Mage", 60}
• 빈 초기화 : new int{}처럼 비워두면 new int()와 마찬가지로 0으로 값이 초기화됩니다. 이 방식이 더 권장됩니다.
• 가장 큰 장점 (안정성) : 중괄호 {} 초기화는 좁아지는 변환을 컴파일 시점에서 엄격하게 차단합니다. 예를 들어, new int{3.14} 를 시도하면 컴파일 오류가 발생하여 데이터 손실 버그를 사전에 방지할 수 있습니다.
3. 클래스 객체 초기화의 공통점
클래스나 구조체(Player 타입)를 초기화할 때는 new Player("Warrior", 55)든 new Player{"Mage", 60}든, 인수에 맞는 생성자가 클래스에 정의되어 있어야 한다는 기본 원칙은 같습니다. 두 방식 모두 정의된 생성자를 호출합니다.
다만, 중괄호 {} 는 생성자가 없는 아주 구조체나 클래스의 경우에만 멤버를 순서대로 채워주는 집단 초기화라는 특별한 기능을 수행합니다.
struct Position
{
int x;
double y;
};
// Position 에 기본 생성자가 없는 경우,
// x에 10을, y에 5.5를 자동으로 넣어줍니다.
new Position{10, 5.5};
4. 코드 예시
기존의 괄호 () 방식과 모던 C++ (C++11 이후) 의 중괄호 { } 방식의 new 할당 및 초기화하는 코드를 예제로 살펴보겠습니다.
#include <iostream>
#include <string>
#include <memory> // 스마트 포인터를 위해 필요
// 예제용 클래스 정의
class Player {
public:
std::string name;
int level;
// 생성자
Player(std::string n, int l) : name{n}, level{l}
{
std::cout << "[생성] Player 객체 생성됨: " << name << std::endl;
}
void printInfo()
{
std::cout << "이름: " << name << ", 레벨: " << level << std::endl;
}
};
int main() {
std::cout << "=== 1. 기본 자료형(Primitive Types) 초기화 ===" << std::endl;
// 1-1. 초기화 없이 할당 (쓰레기 값이 들어있을 수 있음)
int* ptr1 = new int;
*ptr1 = 10; // 나중에 값 대입
std::cout << "ptr1 값: " << *ptr1 << std::endl;
// 1-2. 직접 초기화 (괄호 () 사용) - 가장 일반적
int* ptr2 = new int(20);
std::cout << "ptr2 값: " << *ptr2 << std::endl;
// 1-3. 유니폼 초기화 (중괄호 {} 사용, C++11 이상)
int* ptr3 = new int{30};
std::cout << "ptr3 값: " << *ptr3 << std::endl;
// 1-4. 값 초기화 (빈 괄호 () 또는 중괄호 {} 사용 -> 0으로 초기화됨)
int* ptrZeroA = new int(); // 전통적인 방식: 0으로 초기화
int* ptrZeroB = new int{}; // 모던 C++ 방식: 0으로 초기화
std::cout << "ptrZeroA 값(0 초기화): " << *ptrZeroA << std::endl;
std::cout << "ptrZeroB 값(0 초기화): " << *ptrZeroB << std::endl;
std::cout << "\n=== 2. 배열(Array) 초기화 ===" << std::endl;
// 2-1. 초기화 없이 배열 할당 (각 요소는 쓰레기 값이 들어있을 수 있음)
int* arr1 = new int[3];
arr1[0] = 100; arr1[1] = 200; arr1[2] = 300;
std::cout << "arr1[1]: " << arr1[1] << std::endl;
// 2-2. 모든 요소를 0으로 초기화 (두 방식 모두 사용 가능)
int* arrZeroA = new int[5](); // 전통적인 방식: 모든 요소 0으로 초기화
int* arrZeroB = new int[5]{}; // 모던 C++ 방식: 모든 요소 0으로 초기화
std::cout << "arrZeroA[3] (0이어야 함): " << arrZeroA[3] << std::endl;
std::cout << "arrZeroB[3] (0이어야 함): " << arrZeroB[3] << std::endl;
// 2-3. 특정 값으로 초기화 (C++11 이상)
// 크기가 3인 배열을 만들고 1, 2, 3으로 초기화
int* arrInit = new int[3]{1, 2, 3};
std::cout << "arrInit[2]: " << arrInit[2] << std::endl;
std::cout << "\n=== 3. 객체(Object) 초기화 ===" << std::endl;
// 생성자를 호출하여 초기화
// 참고: 클래스/객체는 ( ) 또는 { } 모두 인수에 맞는 생성자가 정의되어 있어야 함.
Player* p1 = new Player("Warrior", 55);
p1->printInfo();
// C++11 유니폼 초기화 스타일
// (1) 현재 Player처럼 '생성자가 있는 경우', 해당 생성자를 호출함.
// (2) 생성자가 없는 'Aggregate' 타입인 경우, 내부 멤버를 순서대로 채움 (집단 초기화).
Player* p2 = new Player{"Mage", 60};
p2->printInfo();
std::cout << "\n=== 4. 중요: 메모리 해제 (delete) ===" << std::endl;
// new로 할당한 메모리는 반드시 delete로 해제해야 메모리 누수(Memory Leak)를 방지합니다.
// 단일 변수/객체 해제
delete ptr1;
delete ptr2;
delete ptr3;
delete ptrZeroA;
delete ptrZeroB;
delete p1;
delete p2;
// 배열 해제 (반드시 delete[] 를 사용해야 함)
delete[] arr1;
delete[] arrZeroA;
delete[] arrZeroB;
delete[] arrInit;
std::cout << "모든 raw 포인터 메모리 해제 완료." << std::endl;
std::cout << "\n=== 5. 모던 C++ 추천 방식 (스마트 포인터) ===" << std::endl;
// 최근 C++에서는 new/delete를 직접 쓰기보다 스마트 포인터를 권장합니다.
// 자동으로 메모리를 해제해줍니다.
// std::make_unique<타입>(생성자 인자)
// 참고: make_unique는 함수 호출이므로 ( )를 사용해야 하며, 내부적으로도 new T(args) 방식을 사용합니다.
std::unique_ptr<int> smartInt = std::make_unique<int>(999);
std::cout << "스마트 포인터 int 값: " << *smartInt << std::endl;
std::unique_ptr<Player> smartPlayer = std::make_unique<Player>("Archer", 10);
smartPlayer->printInfo();
// delete를 호출할 필요가 없음 (함수 종료 시 자동 해제)
return 0;
}'C++' 카테고리의 다른 글
| 베스트 프랙티스(Best Practice) : 전문적인 C++ 코드의 기준 (0) | 2026.01.10 |
|---|---|
| 객체 초기화 방법 (0) | 2026.01.09 |
| 스택(Stack)과 힙(Heap) : 책임과 수명의 차이 (0) | 2026.01.08 |
| 복사의 종류와 차이점 (0) | 2026.01.08 |
| 매크로란 무엇인가? (0) | 2026.01.05 |