개발자 면접 공부/C-C++

유니폼 초기화

chogyujin 2024. 3. 6. 18:39
728x90

1. 개요

오늘은 C++의 유니폼 초기화에 대해서 공부해보겠습니다.


2. 개념

유니폼 초기화(균일한 초기화)라고 말을 합니다. 아래의 코드를 봅시다.

#include <iostream>

class A 
{
public:
	A() 
	{
		std::cout << "A 의 생성자 호출!" << std::endl; 
	}
};

int main() 
{
	A a();
}

성공적으로 컴파일을 하였으면?

놀랍게도 출력이 없습니다.
도대체 왜 이런 현상이 나오는것일까요?

A a(); //?

왜냐하면 사실은 위 코드가 A 의 객체 a를 만든것이 아니라, A 를 리턴하고, 인자를 받지 않는 함수 a 를 정의한 것이기 때문입니다.
왜냐하면 C++ 의 컴파일러는 함수의 정의처럼 보이는 것들은 모두 함수의 정의로 해석 하기 떄문입니다.

아래의 코드는 더욱 햇갈릴겁니다.

#include <iostream>

class A 
{
public:
	A() 
	{
		std::cout << "A 의 생성자 호출!" << std::endl; 
	}
};

class B
{
public:
	B(A a)
	{
		std::cout << "B 의 생성자 호출!" << std::endl;
	}
};

int main() 
{
	B b(A());
}

성공적으로 컴파일을 할 경우

??? 아무런 출력이 없네요?

사실 위 코드를 보면 마치 b 라는 클래스 B 의 객체를 생성하는 것 같이 보이지만, 사실은 인자로 A 를 리턴하고 인자가 없는 함수를 받으며, 리턴 타입이 B인 함수 b를 정의한 것 입니다.

상당히 이상합니다. 이러한 문제가 발생하는 것은 () 가 함수의 인자들을 정의하는데도 사용되고, 그냥 일반적인 객체의 생성자를 호출하는데도 사용되기 떄문입니다.

그래서 우리의 C++는 11 버전 부터 균일한 초기화 즉 유니폼 초기화라는 것을 도입하게 됩니다.


3. 유니폼 초기화(균일한 초기화)

균일한 초기화 문법을 사용하기 위해서는 생성자를 호출하기 위해 ()를 사용하는 대신에 {} 를 사용하면 끝입니다.

#include <iostream>

class A 
{
public:
	A() 
	{
		std::cout << "A 의 생성자 호출!" << std::endl; 
	}
};

int main() 
{
	A a{};
}

위와 같이 제대로 생성자가 호출되었음을 알 수 있습니다.

중괄호를 이용해서 생성자를 호출하는 문법은 동일합니다.
그냥 기존에 () 자리를 {} 로 바꿔주기만 하면 됩니다.
하지만 ()를 이용한 생성과 {}를 이용한 생성의 경우 한 가지 큰 차이가 있는데 바로 일부 암시적 타입 변환들을 불허하고 있다는 점 입니다.

#include <iostream>

class A 
{
public:
	A(int a) 
	{
		std::cout << "A 의 생성자 호출!" << std::endl; 
	}
};

int main() 
{
	A a1(3.5);
	A a2{3.5};
}

이러면?

이러한 컴파일 오류가 나옵니다.

보시다시피

A a1(3.5); // 가능

위 코드는 성공적으로 컴파일 되었고 a 에는 3.5의 정수 캐스팅 하여 3이 전달됩니다.

반면에

A a1{3.5}; // 불 가능

이친구는 불가능 합니다.
그 이유는 중괄호를 이용해서 생성자를 호출하는 경우 아래와 같은 암시적 타입 변환들이 불가능해집니다. 이들은 전부 데이터 손실이 있는 변환입니다.

  • 부동 소수점 타입에서 정수 타입으로의 변환 (우리의 예시지요)
  • long double 에서 double 혹은 float 으로의 변환, double 에서 float 으로의 변환.
  • 정수 타입에서 부동 소수점 타입으로의 변환

등등이 있습니다.

따라서 {}를 사용하게 된다면, 위와 같이 원하지 않는 타입 캐스팅을 방지해서 미연에 오류를 잡아낼 수 있습니다.

{}를 이용한 생성의 또 다른 쓰임새로 함수 리턴 시에 굳이 생성하는 객체의 타입을 다시 명시 하지 않아도 됩니다.


4. Ref

https://modoocode.com/286

 

씹어먹는 C++ - <16 - 1. C++ 유니폼 초기화(Uniform Initialization)>

유니폼 초기화 ({} 를 이용한 생성자 호출) 를 통해서 인자 없는 생성자가 함수의 정의로 오해되는 일을 막을 수 있으며 initializer_list 를 만들어 전달할 수 있습니다. initializer_list 를 통해서 객체

modoocode.com

 

'개발자 면접 공부 > C-C++' 카테고리의 다른 글

VTable 위치 파악  (0) 2024.03.25
초기화자 리스트(intializer_list)  (0) 2024.03.08
C++ vs C#  (1) 2024.02.18
Move Semantics  (0) 2023.10.29
스마트 포인터의 new와 make의 차이  (1) 2023.10.03