티스토리 뷰

출처 : Effective C++

 

멤버 초기화 리스트가 무엇인지 모르신다면 이전 포스트를 먼저 읽고 오시면 이 글을 읽는데 더욱 잘 이해될 것 같습니다.

이전 포스트에서는 멤버 초기화 리스트를 사용함으로 얻는 장점은 생성자 호출 + 대입으로 이어지는 함수 더블 호출을 한 번만으로 가능하게 하여 비용적인 면에서 효율적이다는 것이었습니다.

 

그렇다면 단순히 비용적 효율말고 멤버 초기화 리스트를 써야하는 또 다른 이유가 있을까요?

 

일단, 제일 큰 이유는 객체의 모든 데이터 멤버가 초기화된다는 보장을 받기 위해서입니다.

물론 객체 타입의 멤버(ex) STL - vector)들은 초기화 리스트에 넣지 않아도 컴파일러가 자동으로 기본 생성자를 호출하도록 만들어줍니다.

그러나, 기본 제공 타입(int, double) 같은 경우 초기화 리스트에 넣지 않으면 초기화된다는 보장이 없습니다.
그렇기 때문에, 객체의 모든 멤버가 초기화된다는 보장을 안고 가기 위해 모든 멤버들을 초기화 리스트에 넣자는 것입니다.

물론, 어떤 분들은 "에이 객체인 멤버는 기본 생성자로 초기화할 건데 초기화 리스트에 안 넣어도 어차피 컴파일러가 알아서 해주겠지~"라는 생각으로 기본 제공 타입의 멤버들만 초기화 리스트에 넣어서 사용할 수 있습니다.
틀린 생각은 아니지만 좋지 않은 생각(틀린 것이라고 할 수도 있겠네요.)은 확실한 것 같습니다.


사람은 실수의 동물인지라, 기본 제공 타입의 멤버들만 초기화 리스트에 넣는다고 해도 실수로 기본 제공 타입 멤버 몇 개 빼먹고 말았습니다.

아래와 같이요.
예를 들어 사람의 정보를 나타내는 기본적인 Person 클래스가 있다고 합시다.

class Person
{
public:
  Person(std::string& name, int age, double height);
  
private:
  std::string name_;
  int age_;
  double height_;
}

Person::Person(std::string& name, int age, double height)
  : age_(age)
{
  ...
}


애석하게도, height_에 대한 초기화를 빼먹고 말았습니다.
만약 생성자 본문 안에서 뒤늦게라도 대입(assignment)을 해준다면 다행이지만, 그것마저 안 되어있다면 나중에 height_을 사용하는 부분에서 사용자가 원하던 동작이 일어나지 않을 수 있습니다.

기본 제공 타입의 멤버에 대해선 사용자가 직접 초기화를 하지 않으면 초기화된다는 보장이 없기 때문이죠.

 

그래서 이런 바보 같은 짓을 미연에 방지하고자 모든 멤버들은 초기화 리스트에 넣어서 초기화하자는 것이죠.

그러면 적어도 "아~ 모든 멤버들을 초기화 리스트에 넣었으니 모두 다 초기화 되었겠지"라는 보장을 안고 갈 수 있으니까요.



두 번째로, 멤버가 상수나 참조자인 경우에는 무조건 초기화 리스트에 넣어줘야 하기 때문입니다.
C++ 문법에 대해 조금 공부해보신 분이라면 상수나 참조자는 초기화만 가능하고 중간에 대입으로서 변경을 할 수 없다는 것을 알고 계실 것입니다.

예로, 아래와 같은 클래스가 있다고 합시다.

class Foo
{
public:
  Foo(int intVal, A& ref);
  
private:
  const int iAmConst;  // 상수
  A& a;                // 참조자
}

Foo::Foo(int intVal, A& ref)
{
  iAmConst = intVal;  // 컴파일 에러! 상수는 대입이 불가능합니다!
  a = ref;            // 컴파일 에러! 참조자는 대입이 불가능합니다!
}

위와 같이 코드를 작성한다면 컴파일 타임에 에러를 뿜어댈 것입니다.

그럼 어떻게 해결할 수 있을까요?
초기화를 해주는 초기화 리스트를 쓰면 됩니다!

class Foo
{
public:
  Foo(int intVal, A& ref);
  
private:
  const int iAmConst;  // 상수
  A& a;                // 참조자
}

Foo::Foo(int intVal, A& ref)
  : iAmConst(intVal),  // OK, 대입이 아니라 초기화!
    a(ref)             // OK, 대입이 아니라 초기화!
{
}

초기화 리스트에 넣는다는 것은, 대입이 아닌 초기화를 수행한다는 것이므로 컴파일 상 아무런 문제가 없습니다.
따라서, 상수 멤버나 참조자 멤버는 무조건 뒤도 볼 것도 없이 초기화 리스트를 써야 합니다.

 

이젠 확실히 아시겠죠?
멤버 초기화 리스트를 사용해야 하는 이유는

  1. 객체의 모든 멤버가 확실히 초기화되었다는 보장을 받아 미정의 동작을 방지하기 위해
  2. 상수나 참조자 멤버는 초기화만 가능하기 때문에

만약, 지금까지 생성자 본문에서 대입을 써오셨다면 시간 나실 때 한번 멤버 초기화 리스트로 바꿔보시는 것도 좋을 것 같습니다 :)

'C++' 카테고리의 다른 글

멤버 초기화 리스트란?  (0) 2019.11.03
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함