Modern C++ - 초기화

5 minute read

Object Initilization

  • C++98/03 의 문제점
    • 변수 종류에 따라 초기화 방법이 다름
    • 배열 초기화 불가
    • 클래스 멤버
    • 동적 메모리 할당
  • Uniform Initialization
    • brace init
    • 객체의 종류에 상관없이 중괄호 {} 를 사용해서 초기화
    • 데이터 손실 방지 prevent narrow
class Test
{
	int x[3] { 1, 2, 3 };
};

int main()
{
	Test t;
	int* p = new int[3] { 1, 2, 3 };
	vector<int> v { 1, 2, 3 };

	// direct initialization
	int n2(0);
	int n1    { 0 };
	int a1[3] { 1, 2, 3 };
	Str s1    { 1, 2 };
	Cls c1    { 1, 2 };

	// copy initialization
	int n2    = 0;
	int n2    = { 0 };
	int a2[3] = { 1, 2, 3 };
	Str s2    = { 1, 2 };
	Cls c2    = { 1, 2 };

	int no = 1.2;     // ok
	int ne = { 1.2 }; // error
	char co = 300;    // ok
	char ce = { 300 };// error
}

초기화 종류

Default Initialization

  • Test t1;
  • new Test;

Value Initialization

  • Test();
  • Test{};
  • Test t1{};
  • new Test();
  • new Test{};
  • Test::Test : data() {}
  • Test::Test : data{} {}

Default VS Value

int n1;   // default initialization, 쓰레기값
int n2{}; // value initialization,   0
int n3(); // 함수 선언

int* p1 = new int; // default, 쓰레기값
int* p2 = new int(); // value, 0
int* p3 = new int{}; // value, 0

class Test1
{
	int x;
	Test1() = default;
};
class Test2
{
	int x;
	Test2() {}
};

Test1 t1;   // default init, 쓰레기값
Test1 t2{}; // value init, 0
Test2 t3{}; // value init, 쓰레기값

Direct Initialization

  • Test t1(1, 2);
  • Test t1{ 1, 2 }; // non-class type with a single brace-enclosed initializer
  • Test(1, 2) // prvalue???
  • Test(t1)
  • new Test(1,…) // ???
  • Test::Test(int a) : data(a) {}
  • a { };
  • static_cast(t1) // prvalue???

Copy Initialization

  • Test t1 = t0;
  • Test t2 = { t0 }; // (until C++11)
  • foo(t0);
  • return t0;
  • throw t0;
  • catch (Test t0)
  • Test arr[2] = { t1, t2 };

List Initialization

  • direct-list-initialization
    • Test t1 { 1, 2 };
    • Test { 1, 2 }
    • new Test { 1, 2 }
    • Class { Test data{ 1, 2 }; };
    • Class::Class() : data{ 1, 2 } {}
  • copy-list-initialization
    • Test t2 = { 1, 2 };
    • foo( { 1, 2 } );
    • return { 1, 2 };
    • object[ { 1, 2 } ] // operator[ x ] 의 x 를 초기화
    • object = { 1, 2 }
    • U( { 1, 2 } ) // U::U(initializer_list)
    • Class { Test data = { 1, 2 }; };
  • std::initilizer_list
#include <initializer_list>

void foo( initializer_list<int> list)
{
	auto p = begin(list); // 상수반복자
	*p = 20; // error

	while( p != end(list) )
		cout << *p++ << endl;

	for (auto n : list)
		cout << n << endl;
}

int main()
{
	initializer_list<int> s = { 1, 2, 3, 4, 5 };
		// cl  int *first, *last
		// g++ int *first, count

	foo( { 1, 2, 3 } );
}
  • 생성자 파라미터로 받기
class Test
{
public:
	Test(int a, int b) {}
	explicit Test(double a, double b) {}
	Test(initializer_list<int>) {}
};

int main()
{
    Test t1( 1, 2 );    // Test(int, int)
    Test t2( 1, 2, 3 ); // error

	Test t3( { 1.1, 2.2 } ); // error explicit
		// 변환생성자로 임시객체 생성, 복사생성자

	Test t4{ 1, 2 }; // init -> i, i
	Test t5 = { 1.1, 2.2 }; // init -> d, d, error explicit

	Test t6{1, 2, 3};    // init
	Test t7 = {1, 2, 3}; // init
}
  • STL 사용시 주의사항
    • () 와 {} 가 다르게 동작한다.
template<typename T, typename Ax = allocator<T>>
class vector
{
	T* buff;
public:
	vector(size_t sz, T v = T() ) {}
	vector(initializer_list<T> s) {}
};
int main()
{
	vector<int> v = { 1, 2, 3 } ;
	vector<int>(5, 3); // { 3, 3, 3, 3, 3 }
	vector<int>{5, 3}; // { 5, 3 }
}

Aggregate Initialization

  • Test t1 = { 1, 2 };
  • Test t2 { 1, 2 }; // C++11
  • Test t3(1, 2); // C++20
  • Test t4 = { .v1 = 1, .v2 { 2 } }; // C++20
  • Test t5 { .v1 = 1, .v2 { 2 } }; // C++20

  • Aggregate Type : {} 로 초기화 가능한 것
    • 배열
    • 구조체 (class, struct, union)
      • 생성자가 없어야 함
        • C++11 : default, delete 허용
        • C++17 :
        • C++20 :
      • default member initializer 없어야 함 (since C++11 until C++14)
      • 상속 없어야 함
        • C++17 : virual, private, protected 상속이 불가, public 은 됨, 또 있나??
      • 가상함수 없어야 함
struct Base {};
struct Test : Base
{
    int x, y;

    Test() = default;
};

int main()
{
    Test t1;
    Test t2 { Base(), 1, 2 };
    Test t3 = { Base(), 1, 2 };
}
  • Designated Initializers
    • C 에만 있고 C++ 에서는 없어졌으나 C++20 에서 부활하는 듯
    • 변수 순서 맞춰줘야 함, 중간에 빼먹는 것은 가능
    • Narrowing conversions 불가
struct A
{
	int v1;
	int v2;
	int v3 = 3;
	int v4;

	/* 아직 제대로 설명이 된 곳을 찾기 힘들고 테스트 환경도 없음-_-
	union
	{
		int u_i;
		doubld u_d;
	}
	*/
};

A a { .v2 = 2, .v1 = 1 }; // error, 순서 맞춰줘야 함
A b { .v1 = 1, .v4 = 4 }; // ok, v2 = 0, v3 = 3;

union u
{
	int a;
	const char* b;
};

u f = { .b = "asdf" };
u g = { .a = 1, .b = "asdf" }; // error, union은 하나만...

Reference Initialization

  • Test &t1 = t0;
  • Test &t2(t0);
  • Test &t3 = { 1, 2 };
  • Test &t4 { 1, 2 } ;

  • Test &&t1 = t0 ;
  • Test &&t2 ( t0 ) ;
  • Test &&t3 = { 1, 2 };
  • Test &&t4 { 1, 2 } ;

  • void foo( Test & arg ); or void foo( Test && arg );
    • foo( t0 )
    • foo( { 1, 2 } )
  • Test& foo() or Test&& foo()
    • return t0;
  • Test& data; or Test&& data;
    • Class::Class(…) : data(t) {}

초기화 리스트, default member initializer

  • 초기화 리스트 먼저 체크
  • 그다음 default member initializer
int c = 0;
class Test
{
public:
	int data = ++c;
	int x[3];

	Tes t() {}
	Test(int n) : data(n) {}
	Test(double) {}
};

int main()
{
	Test t1;      // data = 1
	Test t2(10);  // data = 10
	Test t3(1.2); // data = 2
}

explicit

  • 변환생성자 불가
  • copy initialization 불가
class Test
{
	int x, y;
public:
	explicit Test() {}
	explicit Test(int a) : x(a) {}
	explicit Test(int a, int b) : x(a), y(b) {}
};
int main()
{
	Test t1(1);
	Test t2 = 2; // error, explicit
	Test t3(1, 2);
	Test t4 = (1, 2); // error
	Test t5{1, 2};
	Test t6 = {1, 2}; // error, explicit

	Test t7;
	Test t8{};
	Test t9 = {}; // error, explicit
}