객체 초기화
Effective C++ 공부 내용입니다.
객체 초기화
-
C++에서 언제 객체의 초기화가 보장되며 언제 그렇지 않은지에 규칙이 명확히 정해져있다.
-
기본적으로 C문법을 사용하는 경우 값이 초기화된다는 보장이 없지만, C++ 문법에서는 상황에 따라 달라진다.
- 그냥 배열(C)는 확실히 초기화된다는 보장이 없으나, vector(C++)은 보장을 받는 이유가 이러한 법칙 때문이다.
- 어쩔 수 없이 가장 좋은 방법은 모든 객체를 사용하기 전에 항상 초기화를 해주자.
- 비멤버 객체에서 기본제공 타입의 경우 초기화를 손수 직접해야 한다.
- 이런 부분을 지외하고 나면 C++ 초기화의 나머지 부분은 생성자로 귀결된다.
- 생성자에서 지킬 규칙은 해당 객체의 모든 것을 초기화하는 것만 신경쓰면 된다.
- 쉬워 보이지만 대입과 초기화를 헷갈리지 않는 것이 중요하다.
class A {};
class B {
public:
B(const string& name, const string& adress);
private:
string m_Name;
string m_Address;
};
B::B(const string& name, const string& adress) {
m_Name = name;
m_Address = adress;
}
- 해당 예시는 초기화가 아니라 대입을 하고 있다.
- 내가 만든 생성자가 호출되기 전에 디폴트 생성자가 이미 초기화를 하였고, 그 후에 대입이 진행되고 있다.
- 대부분의 데이터 타입에 대해서는 기본 생성자 호출 후 복사 대입 연산자를 연달아 호출하는 해당 방법보다,
- 복사 생성자를 한 번 호출하는 쪽이 더 효율적이다.
- 여기에서 대부분의 데이터 타입에 포함되지 않는 타입은 기본 제공 타입이다.
- 그렇다면 어떻게 해야 할까?
- 대입문 대신 member initializer lists를 사용하면 된다.
- 대입만 사용한 버전의 경우 디폴트 생성자의 초기화를 하고, 곧바로 새로운 값을 대입하고 있는 문제를 해결할 수 있다.
- member initializer lists에 사용되는 인자가 생성자의 인자로 쓰이기 때문이다.
- 대입만 사용한 버전의 경우 디폴트 생성자의 초기화를 하고, 곧바로 새로운 값을 대입하고 있는 문제를 해결할 수 있다.
- 대입문 대신 member initializer lists를 사용하면 된다.
B::B(const string& name, const string& adress) : m_Name(name), m_Address(adress) {}
- 사용자가 원하는 값을 주고 시작한다는 점에서 똑같지만, 해당 생성자는 앞선 생성자보다 효율적일 가능성이 크다.
- 데이터 멤버가 상수이거나 참조자로 되어 있는 경우에는 반드시 초기화를 해야 한다.
- 왜냐면 상수와 참조자는 대입 자체가 불가능하기 때문이다.
- 공부를 하다 보니, 어떤 것은 초기화를 해줘야 하고 어떤 것은 괜찮고 다 외우기에는 무리가 있다.
- 그래서 기본적으로 member initializer lists을 사용을 하는 것이다.
- 하지만 초기화 리스트가 굉장히 길어져서 보기 힘든 경우가 있다.
- 이경우 대입을 통하여 초기화가 가능한 것은 초기화 리스트에서 빼내어 하나의 함수에 몰아놓고,
- 모든 생성자에서 이를 호출하는 것도 나쁘지 않다.
- 이는 초기값을 파일에서 읽는다든지 데이터베이스에서 찾는 경우에 특히 유용할 수 있다.
객체를 구성하는 데이터의 초기화 순서
- 어떤 컴파일이든 객체의 데이터를 초기화하는 순서는 항상 똑같다.
- 기본 클래스는 파생 클래스보다 먼저 초기화
- 클래스 데이터 멤버는 그들이 선언된 순서대로 초기화
- 여기서 중요한 것은 정적 객체이다.
- 정적 객체에도 종류가 있다고 한다.
- 전역 객체
- 네임스페이스 유효범위에서 정의된 객체
- 클래스 안에서 static으로 선언된 객체
- 함수 안에서 static으로 선언된 객체
- 파일 유효범위에서 static으로 정의된 객체
- 4번은 local static object(지역 정적 객체)라고 하고, 나머지는 non-local static object(비지역 정적 객체)라고 한다.
- translation unit(번역 단위)는 컴파일을 통해 하나의 object file(목적 파일)을 만드는 바탕이 되는 소스 코드를 일컫는다.
- 여기서 번역은 소스 코드를 기계어로 옮긴다는 의미이다.
- 여기서 중요한 점은 비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다.
- 번역 단위가 다르다면 각각의 번역 단위에서 정의된 비지역 정적 객체들의 초기화 순서가 정해져있지 않다.
- 별도로 컴파일된 소스 파일이 두 개 이상 있고, 각 소스 파일에 비지역 정적 객체가 한 개 이상 있다고 가정하자.
- 어떤 번역 단위의 비지역 정적 객체의 초기화가 진행되면서, 다른 쪽 번역 단위에서 해당 비지역 정적 객체를 사용하게 된다면
- 초기화가 되어있지 않은데 사용하려고 할 수 있다.
- 해결 방법은 non-local static object를 함수 안에서 정적 객체로 선언하여 해당 객체에 대한 참조자를 반환하는 것이다.
- 함수 안에서 선언을 다시 했기에 local static object로 판단된다.
- C++ 규칙 중 하나로 지역 정적 객체는 해당 객체 정의에 최초롤 도달하면 초기화되도록 만들어져있다.
- 이를 이용하여 반드시 초기화된 정적 객체를 얻는 것이다.
- 함수 안에서 선언을 다시 했기에 local static object로 판단된다.
정리
- 어떤 객체가 초기화되기 전에 그 객체를 사용하는 일이 생기지 않도록 하기 위한 3가지 스킬
- 멤버가 아닌 기본제공 타입 객체는 직접 초기화하자.
- 객체의 모든 부분에 대한 초기화는 member initializer lists을 사용하자.
- 별개의 translation unit에 정의된 non-local static object에 영향을 생각하고 프로그램을 설계하자
댓글남기기