생성자에서의 초기화
이것이 왜 좋은 코드인지 알아보자.
class A { // Good
string s1;
public:
A() : s1{"Hello, "} { } // GOOD: directly construct
// ...
};
일전에 대입과 초기화에 대한 내용을 정리했었는데, 초기화와 별도로 대입을 하면 user defined type은 함수가 호출되는 동작을 추가로 하게 된다. 아래코드에 B 클래스는 얼핏 보면 생성자 안에서 바로 s1을 초기화했지만 사실 이것은 초기화를 한 후 대입을 한 것이다.
class B { // BAD
string s1;
public:
B(const char* p) { s1 = p; } // BAD: default constructor followed by assignment
// ...
};
컴파일러가 위 코드를 아래와 같이 코드를 생성을 한다.
//컴파일러가 생성한 코드
class B
{
std::basic_string<char> s1;
public:
inline B(const char * p) : s1{std::basic_string<char>()}
{ this->s1.operator=(p); }
};
s1{std::basic_string<char>()}
B 클래스가 생성되는 시점에 컴파일러가 str::string() 디폴트 생성자를 생성하고, 생성자 안에서는 대입연산자로 대입하여 함수가 두 번 호출되게 된다.
컴파일러에서 만들어준 문법처럼 아래와 같이 생성자() 뒤에 ':'을 적고 s1{p} 혹은 s1(p) 형태로 나열하면 이것은 대입이 아니라 초기화가 되기에 좋은 코드이다.
class A { // Good
string s1;
public:
A(string p) : s1{p} { } //or s1(p)
// ...
};
이렇게 하면 아래코드처럼 p값이 초기화되기 이전에 호출되는 오류를 예방도 할 수 있다.
class C { // UGLY, aka very bad
int* p;
public:
C() { cout << *p; p = new int{10}; } // accidental use before initialized
// ...
};
📜 그래서 C++핵심 가이드라인에서는 아래와 같이 가이드하고 있다.
C.49: 생성자 안에서의 대입보다는 초기화를 선호하라 (C.49: Prefer initialization to assignment in constructors)
멤버변수가 여러 개 일 때 초기화 순서를 알아보자.
아래코드는 멤버변수가 여러 개를 초기화하고 있다.
Q. m1, m2 값은 어떻게 될까?
class Foo {
int m1;
int m2;
public:
Foo(int x) :m2{x}, m1{++x} { }
};
Foo x(1);
m2 = 1, m1 = 2 일까❓
결과는 m2, m1 이 '2'이다. 🤔
왜냐하면 컴파일러가 멤버변수 순서대로 조정시키기 때문에 멤버변수 초기화는 아래와 같이 조정된다. 이렇게 조정된 결과로 m1 = 2, m2 = 2가 된 것이다.
Foo(int x)
: m1{++x}
, m2{x}
위와 같이 단순한 오류는 쉽게 찾을지 모르지만, 아래와 같은 코드는 의도대로 동작하지 않지만 에러도 발생하지 않아 오동작의 원인을 한참 디버깅해야 할지도 모른다.
struct vector
{
int* buff;
int size;
public:
(2) , (1)
vector(int sz) : size(sz), buff(new int[size]) //size 값이 나중에 초기화 된다.
- 기대한 결과 : size 수 만큼 int 배열 메모리 할당
- 실제 결과 : int 메모리 할당
📜 그래서 C++핵심 가이드라인에서는 아래와 같이 가이드하고 있다.
C.47: 멤버 변수들은 선언된 순서대로 초기화하라 (C.47: Define and initialize data members in the order of member declaration)
💬 생성자 관련 응용 내용은 부담스러운 길이가 될 것 같아 별도로 작성하겠다.
참고
* 본 글에 예제 코드는 C++ Core Guidelines 샘플코드를 활용하였습니다.
C++ 핵심 가이드라인 한글화 프로젝트 링크
C++ Core Guidelines 원문 링크
- C.49: Prefer initialization to assignment in constructors
- C.47: Define and initialize data members in the order of member declaration
728x90
'L C++' 카테고리의 다른 글
애증의 const 사용하기 (1) (0) | 2024.05.11 |
---|---|
(응용) 생성자(constructors) 초기화 (16) | 2024.04.21 |
std::move the 이해하기 (40) | 2024.04.12 |
perfect forwarding을 perfect하게 이해하기 (14) | 2024.03.29 |
std::string_view의 이해 (0) | 2024.03.19 |