센로그

[EC++] 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자 본문

Effective/Effective C++

[EC++] 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자

seeyoun 2024. 11. 13. 16:05

객체의 초기화가 중요한 이유는?

  • 예기치 못한 상황을 방지하기 위해서.
  • C++의 객체 초기화 규칙은 랜덤인가?
    • 아니다. 언제 초기화가 보장되고 아닌지 명확히 정해져있다.
    • 그러나 전부 기억하기에는 규칙이 너무.. 복잡하다.
  • 따라서 가장 좋은 방법은, 모든 객체를 사용 전에 항상 초기화하는 것이다.

 


생성자 - 대입을 통한 초기화 vs 초기화 리스트

  • 대입을 통한 초기화
    • 기본 생성 후에 대입이 이루어진다. 따라서 멤버 변수가 두 번 초기화된다.
      • 기본 생성 후, 복사 대입 연산자를 통해 복사.
        • 클래스 타입의 멤버변수의 경우, 해당 클래스의 기본 생성자가 호출되어 초기화된다. 
        • 이후에 생성자 내부에 있는 대입 연산을 통해 덮어씌워 지는 것이다.
      • 단, 내장 타입은 별도의 초기화가 없다.
    • const 멤버나 & 멤버는 대입이 불가능하므로, 대입을 통해 초기화할 수 없다.
  • 초기화 리스트
    • 객체가 생성될 때 초기화 리스트를 통해 멤버 변수에 값을 할당한다.
      • 기본 생성 시 복사 생성자를 통해 복사
        • 클래스 타입의 멤버변수가 초기화 리스트에 있다면, 초기화 리스트에 명시된 방식으로 생성자가 호출되어 초기화된다.
    • 즉, 멤버 변수들이 객체가 메모리에 할당되는 즉시 원하는 값으로 초기화된다. (한 번만 초기화)
    • const 멤버나 & 멤버도 초기화리스트를 통해 초기화할 수 있다.

 


기억해야할 C++ 객체 초기화 순서

  • 기본 클래스는 파생 클래스보다 먼저 초기화된다.
  • 클래스 데이터 멤버는 선언된 순서대로 초기화된다.
    • 초기화 리스트에서 순서가 달라져도 선언 순서대로 초기화됨
    • 헷갈림 방지를 위해 초기화 리스트는 선언 순서와 맞추자

 


비지역 정적 객체의 초기화 순서 문제

  • 한 번역 단위 내에 있는 비지역 정적 객체들은 코드 작성 순서에 따라 초기화된다.
  • 그러나, 별개의 번역 단위에서 정의된 비지역 정적 객체들의 초기화 순서는 '정해져 있지 않다.'
    • 변역 단위란 컴파일되는 단위를 의미한다.
    • 따라서, 초기화 이전에 다른 번역 단위의 비지역 정적 객체에 의해서 참조될 수 있다! 
    • 이런 경우, 비지역 정적 객체를 지역 정적 객체로 바꾸면 된다.
  • 지역 정적 객체는 해당 함수가 처음 호출될 때 초기화되므로, 초기화 순서 문제를 피할 수 있다.
// file1.cpp
#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor called" << std::endl;
    }
    void doSomething() const {
        std::cout << "Doing something" << std::endl;
    }
};

// 비지역 정적 객체 대신, 정적 객체를 반환하는 함수 사용
MyClass& getMyClassInstance() {
    static MyClass instance; // 함수 내 지역 정적 객체, 첫 호출 시 초기화됨
    return instance;
}
// file2.cpp
#include <iostream>

extern MyClass& getMyClassInstance();

void useMyClass() {
    getMyClassInstance().doSomething(); // 초기화 순서 문제 없이 안전하게 사용 가능
}

 


나의 생각

 

+) 내장 타입 멤버변수도 초기화 리스트에 넣는 것이 좋은가?

그렇다. 성능상 이점은 없을지라도, 다음의 이점들이 있다.

  • 초기화가 보장됨
  • 일관성 있게 관리 가능
  • const도 초기화 가능

같은 이유로, 어떤 멤버변수를 기본 생성자로 초기화하고 싶은 경우에도 초기화 리스트를 사용하도록 한다.

 

+) 비지역 정적 객체 vs 지역 정적 객체

정적 객체
: 생성 시점부터 프로그램이 끝날 때까지 살아있는 객체

  • 비지역 정적 객체
    : 프로그램 시작 시 생성되며, 프로그램 종료 시 소멸된다. 
    • 전역 객체
    • 네임스페이스에 있는 객체
    • 클래스 안에서 static으로 선언된 객체
    • 파일 유효 범위에서 static으로 선언된 객체
  • 지역 정적 객체
    : 해당 함수가 처음 호출될 때 초기화되며, 프로그램 종료 시 소멸된다.
    • 함수 안에서 static으로 선언된 객체

 

 

 

Comments