객체를 사용하기 전에 반드시 그 객체를 초기화 하자!!


이 항목을 위의 한 줄로 끝나는 단원이라해도 무방하다.

이번 장에서는 고맙게도 3가지의 유의점을 콕 찝어 설명해주고 있다.

하나하나 들여다보면서 진행해보도록 하겠다.


1. 멤버가 아닌 기본제공 타입 변수는 직접 초기화하자!


Tip.

배열의 경우 초기화가 런타임에 비용이 소모될 수 있는

 상황이라면 초기화에 대한 보장이 없다.

하지만 vector는 그러한 보장을 가질 수 있다.


초기화 방법

int x = 0;

const char* text = "C_Style";

double d;

cin >> d;


간단하다.

그냥 변수를 생성했다면 바로 초기화해서 사용하자 이런 뜻이다.



2. 객체의 초기화는 초기화 리스트를 이용하자!!


주의점.

" 대입을 초기화와 헷갈리지 않는 것이 가장 중요하다. "


객체의 초기화는 그렇다. 바로 생성자로 귀결된다.

먼저 위에서 언급했던 대입의 경우를 보도록 하자.



C++ 규칙에 의하면 어떤 객체이든 그 객체의 데이터 멤버는

생성자의 본문이 실행되기 전에 초기화되어야 한다라고 명기되어 있다.


위의 예제는 초기화가 아닌 단순 대입이다.

초기화는 단계는 진작에 지나간 셈이다.


여기서 잠깐!

기본 제공 타입인 hp, mp도 미리 초기화 되었을까?

기본 제공 타입의 경우 대입되기 전에 초기화되리란 보장이 없다.


자 그럼 대입이 아닌 멤버 초기화 리스트를 사용해서

대입문 없이 진짜 초기화를 시켜보도록 하겠다.


초기화리스트를 사용하여 깔끔하게 초기화 완료!


대입의 경우 기본 생성자를 호출해서 초기화를 미리 해놓은 후

복사 대입 연산자를 연달아 호출하는 방법이라 하면

초기화 리스트는 복사 생성자 한 번만을 호출하는 방법이다.

당연히 초기화 리스트의 효율이 더 좋다는 뜻이다.

(대부분의 데이터 타입에서는)




대부분의 타입에 포합되지 않는 타입이

앞에서 말한 hp와 같은 기본제공 타입이다.(int)

기본 제공 타입인 경우 초기화나 대입에 드는

비용이 얼마 차이가 나지 않는다고 한다.


하지만!!

데이터 멤버는 초기화 리스트에 모두 올려주는

정책을 지향하며 실수를 방지하는 것도 좋은 방법일 것이다.




기본 제공 타입의 경우에도 초기화 리스트가

의무화 되어지는 경우가 있다. 어떤 경우냐면

바로 const 혹은 참조자로 되어있는 경우이다.

(상수와 참조자는 대입 자체가 불가능)

이 경우 초기화리스트는 선택이 아닌 필수!


아까도 말했지만 기본 제공 타입은

대입과 초기화의 비용이 별반 차이 나지 않는다.

초기화리스트에 주렁주렁 메달려있는 모습이 보기 싫다면


(초기화 해야하는 생성자가 많을 경우 초기화 리스트 또한 늘어나기 때문)


위와 같이 멤버 함수를 통해 초기화 해주는 방법이 있다.




Tip.

객체를 구성하는 데이터의 초기화 순서

1. 기본 클래스는 파생 클래스보다 먼저 초기화 된다.

2. 클래스 데이터 멤버는 그들이 선언된 순서대로 초기화 된다.


따라서 초기화 리스트 순서대로 초기화 되는 것이 아니라

선언된 순서와 같다. 때문에 혼동을 막기 위해

선언 순서와 초기화 순서를 일치 시키도록 하자!



3. 별개의 번역 단위에 정의된 비지역 정적 객체에

영향을 끼치는 불확실한 초기화 순서를 염두해두자!


이게 도대체 뭔 소린가 싶지만 책을

 찬찬히 읽어보며 해석 해보도록 하자.


들어가기에 앞서 용어 정리


정적객체란?

1. 전역 객체

2. 네임스페이스 유효범위 안의 객체

3. 클래서 내의 static 객체

4. 함수 내의 static 객체

5. 파일 유효범위 내의 static 객체


이들 중 함수라는 지역성을 가진 4번만

지역 정적 객체라고 한다.

위의 5가지의 정적객체는

프로그램이 끝날 때 자동 소멸된다.


번역 단위란?

쉽게 말해 하나의 소스 파일

#include하는 파일들까지 합침




==========================문제 제기


두 개의 번역 단위 중 한 곳에서

1. 비정적 객체를 초기화 하려고 보니

2. 다른 번역단위의 비지역 정적 객체를 필요로 한다.

3. 하지만 이 비지역 정적객체는 초기화 되어 있는지 모른다.


별개의 번역단위에서 정의된 비지역 정적 객채들의

초기화 순서는 정해져 있지 않다!!


===========================해결책


1. 비지역 정적 객체를 하나씩 맡는 함수를 준비하고

2. 이 안에 각 객체를 정적 객체로 선언한다.

3. 그리고 함수는 이들에 대한 참조자를 반환한다,


--> 비지역 정적 객체를 직접 참조하는 폐단은 버리고

이제는 함수호출로 대신하게 된다.

비지역 정적 객체 ---> 지역 정적 객체로 바뀌었다.




무슨 말인가??

지역 정적 객체는 함수 호출 중에 그 객체의 정의에

최초로 닿았을 때 객체를 초기화 해버린다는 것이다.

따라서 비지역 정적 객체에 직접 접근하지 않고


함수 내에서 지역 정적객체를 정의하고 초기화 하여

지역 정적 객체에 대한 참조자를 반환하게 만들었다면

그 참조자는 반드시 초기화된 형태라는 것이다.


번역 단위 1


class File {                                  

public:

size_t nimDisks() const;

 }  


File& tf(){                                   

static File f;                      ----> 2. 지역 정적 객체를 정의 하고 초기화

return f                            ----> 3. 이 객체에 대한 참조자를 반환

}    


번역 단위 2


class Dir { 

...

size_t disks = tf().numDiskes();   ---> 1. 함수를 호출

...

}


Dir& tempDir(){

static Dir td;

return rd;

}

--> 결론

객체에 직접 접근하지 않고 함수를 통해 접근하면 된다.


초기화 순서 문제를 방지하기 위해 이처럼 참조자 반환 함수를

사용하는 것은 객체들이 초기화 순서를 제대로 맞춰 둔다는

전제조건이 뒤받침되어 있어야 가능한 말이다.



POINT!!


그냥 무조건 초기화해서 쓰자!!




내용이 조금 길어진 듯하다.

하지만 책에서 초기화에 대한 내용을

매우 강조하고 있다는 점을 보아

한번쯤 제대로 짚고 넘어가야할 부분인 듯 하다.


+ Recent posts