728x90

다양한 초기화 방법

C++ 에서는 =,(),{} 와 같은 다양한 초기 방법이 있다.

(일반 자료형과 객체 타입 초기화 방식에는 약간의 차이가 있다.)

 

장점

- 축소 변환 방지(컴파일 에러)

- 배열의 초기화 문법 {}와 같이 생성자를 정의할 수 있다.

 

단점

- initializer_list<T> 생성자를 이용하여 stl container vector와 같은 방식으로 초기화 할 수 있다. 이때 일반적인 생성자 시그니처와 initailizer 초기화 방식의 시그니처가 동일한 경우 (), {}에 따라 다른 생성자가 호출된다. 이는 초기화 호출방식에 대한 혼란을 야기시킨다.

 

결론

  • 소괄호 초기화
    • 전통적인 초기화 방식 → 거부가 없음
    • vector 등 특이한 케이스에 대해서면 {} 사용
  • 중괄호 초기화 {}
    • 초기화 문법 일치화(intializer_list<T> 생성자를 이용) ⇒ 100% 대응하진 못함.
    • 축소 변환 방지
  • 개인 생각
    • 중괄호 초기화 방식의 축소변환은 확실히 개발단계에서 장점이지만 이고 개인의 취향에 따른 선택사항이지만 중괄호 초기화로 인해 생성자 호출 방식에 있어서 분기점이 생긴다. 이는 팀단위 개발에서는 코드 가독성을 떨어뜨릴 것 같다. {}초기화는 약속된 특정 클래스에서만 합의하에 사용하는게 좋을 것 같다. 애초에 ms도 그렇게 사용하는 것(?) 같다.
#include <iostream>
#include <vector>

using namespace std;

class Knight
{
public:
	Knight()
	{
		cout << "Knight 기본 생성자" << endl;
	}

	Knight(const Knight& k)
	{
		cout << "Knight 복사 생성자" << endl;
	}


	Knight& operator = (const Knight& k)
	{
		cout << "Knight 대입 연산자" << endl;
		hp = k.hp;
		return *this;
	}

public:
	int hp = 100;

};

class Mage
{
public:
	Mage()
	{

	}

	Mage(int a, int b)
	{
		cout << "Mage(int int)" << endl;
	}

	Mage(initializer_list<int> li)
	{
		cout << "Mage(initailizer_list)" << endl;
	}
};

Knight Func()
{
	return Knight();
}

int main()
{
	// 다양한 초기 방법
	{
		// 일반 자료형
		int a = 0;

		int b(0);
		int c = (0);

		int d{ 0 };
		int e = { 0 };

		// 객체 타입
		Knight k1; // k1 기본 생성자
		Knight k2 = k1; // k2 복사 생성자

		Knight k3; // k3 기본 생성자
		k3 = k1; // k3 대입 연산자

		Knight k4{ k2 }; // k4 복사 생성자.
	}

	// !주의!
	{
		Knight k5(); // knight k5; 이 기본생성자를 호출하는 거고 knight k5()는 함수 선언문이다. void형 매개변수에 리턴값이 knight인 k5.
		Knight k6{}; // 이건 기본생성자 버전.
	}

	// {} 초기화 장점 - 축소 변환 방지
	{
		// 일반 초기화시 축소됨
		double x = 1.34;
		int y = x; // 컴파일에서 경고
		int z = { x }; // 컴파일에서 에러
	}

	// {} 초기화 장점 - 배열과 같은 방식을 객체 초기화 가능.
	{
		// 번거로운 초기화 방식
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);

		// {}를 이용한 초기화 방식. 마치 배열 초기화 방식과 유사
		int arr[] = { 1,2,3,4,5 };

		v1 = { 1,2,3,4 };

	}

	// {} 문제점 - 아래 두 초기화 방식은 다른 생성자를 호출한다. - why?
	{
		vector<int> v1{10 , 1}; // 벡터 크기를 10으로 지정하고 모든 요소를 1로 초기하는 생성자 호출.
		vector<int> v2(10, 1); // 벡터에 10, 1 두 값을 초기화하는 생성자 initalizer_list 생성자 호출.
	}


	// {} 문제점 재현
	{
		Mage m{ 3,4 }; // initailizer 생성자가 정의되어 있으면	Mage(int a, int b) 생성자는 호출되지 않습니다.
	}

	return 0;
}

공부한 내용을 개인적으로 복습하기 쉬운 형태로 정리했습니다.

잘못된 내용이 있는 경우 언제든 댓글 혹은 이메일로 지적해주시면 감사드립니다.

728x90

'Programming Language > C++' 카테고리의 다른 글

std::function  (0) 2022.02.10
Modern C++/lambda/람다  (0) 2022.02.09
using - type alias declaration  (0) 2022.02.06
auto  (0) 2022.02.06
함수 객체(함수자, Functor)  (0) 2022.02.06

+ Recent posts