프로그래밍백엔드 C++ 개발자

C++11 이상에서 member initializers를 통해 클래스 멤버를 초기화하는 방법은 어떻게 되며, 인라인 초기화, 생성자 초기화 목록, 생성자 본문 내에서의 선언은 어떤 차이가 있습니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

문제의 역사:

클래식 C++에서는 클래스 멤버가 오직 생성자 초기화 목록에서만 초기화되었습니다. C++11부터는 코드의 가독성과 안전성을 높이기 위해 클래스 내부 선언에서 기본값을 직접 지정하는 기능(member initializers)이 추가되었습니다.

문제:

클래스 멤버에게 값을 지정하는 여러 가지 방법이 있습니다: 직접 선언(in-class), 생성자 초기화 목록을 통해, 그리고 생성자 본문 내에서. 각 방법은 성능과 의미에 영향을 미치며, 잘못 이해하면 불필요한 복사나 기본 소멸자, 상수 및 참조에 대한 오류를 초래할 수 있습니다.

해결책:

  1. In-class initializer — 간단한 기본값을 위한 권장 방법(오직 C++11+에서 작동). 클래스 내부:
class MyClass { int x = 42; };
  1. 생성자 초기화 목록 — 기본 생성자가 없는 클래스 멤버, 상수 및 참조에 필요:
class MyClass { const int y; MyClass(int val) : y(val) {} // 그렇지 않으면 컴파일 오류 };
  1. 생성자 본문에서 초기화 — 권장되지 않는 방법으로, 이 시점에서 데이터 멤버는 기본 생성자 호출로 이미 생성되어 있으며 불필요한 작업이 발생:
class MyClass { std::string s; MyClass() { s = "hello"; } // 먼저 기본값이 설정되고 후에 할당 };

주요 특징:

  • in-class initializer는 기본값을 설정하는 작업을 간소화합니다.
  • 초기화 리스트는 초기화 순서에 대한 제어를 제공합니다(상수 및 참조에 중요).
  • 생성자 본문에서의 초기화는 비기능적이며 기본 생성자가 없는 클래스 멤버에 대해 안티패턴입니다.

트릭 질문.

클래스에서 멤버의 초기화 순서는 어떻게 결정됩니까? 선언 순서대로, 아니면 초기화 리스트 순서대로?

초기화 순서는 항상 클래스 내에서 멤버가 선언된 순서대로이며, 초기화 리스트의 순서와는 무관합니다. 이 순서를 위반하는 것은 의존적인 멤버에게 위험합니다.

class A { int x = 1; int y = 2; A() : y(10), x(20) {} }; // y는 초기화 리스트의 순서와 관계없이 x보다 나중에 초기화됩니다.

초기화 리스트에서 초기화되지 않은 상수 멤버를 생성자 본문에서 초기화할 수 있습니까?

아니요. 상수는 초기화 리스트에서만 초기화할 수 있습니다. 생성자 본문에서의 할당은 컴파일 오류입니다.

클래스에서 in-class initializer를 사용하여 멤버에 대해 기본값을 지정하고 생성자 초기화 리스트에서 재정의하면 어떻게 됩니까?

생성자 초기화 리스트의 값이 사용됩니다. 기본값은 초기화 리스트가 아무것도 지정하지 않을 때만 사용됩니다.

class C { int x = 10; C() : x(20) {} // x는 20이 됩니다. };

전형적인 오류 및 안티패턴

  • 초기화 리스트 대신 생성자 본문에서 복잡한 멤버를 초기화함.
  • 멤버 정의 순서와 초기화 순서의 위반.
  • 초기화 리스트 외부에서 상수 또는 참조를 초기화하려고 시도함.

실제 사례

부정적인 케이스

클래스가 파일 작업을 수행합니다. 파일은 std::ofstream으로 선언되고 생성자 본문에서 초기화됩니다. 위험: 기본 생성자에서 유효하지 않은 std::ofstream이 생성될 수 있어 파일 작업 중 오류가 발생합니다.

장점:

  • 구현이 간단합니다.

단점:

  • 불필요한 복사 또는 유효하지 않은 객체 생성으로 이어집니다.
  • 상수/참조 멤버에서 오류가 발생합니다.

긍정적인 케이스

파일이 초기화 리스트에서 초기화되어 유효하지 않은 상태의 파일 사용을 방지하며, 기본 데이터를 가진 멤버는 in-class initializer를 사용합니다.

장점:

  • 명확하고 신뢰할 수 있으며 효율적인 객체 생성.
  • 상수/참조에 대한 안전성.
  • 이중 초기화가 없습니다.

단점:

  • 많은 생성자가 있을 경우 초기화 리스트 코드가 중복됩니다.