항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
int x;
초기화 되는가? 상황에 따라 다르다. c++ 객체 초기화는 중구난방이 아니라, 명확한 규칙이 있다.
일단 가장 좋은 방법은 모든객체를 사용전에 항상 초기화하는 것이다. (아래코드 참조)
int x = 0;
const char* text = "test";
c++의 초기화의 나머지부분은 생성자로 귀결된다. 생성자는 객체의 모든 데이터를 초기화 해야한다.
1. 대입과 초기화를 헷갈리지 말자.
class PhoneNumber {};
class ABEntry{
public :
ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones);
private:
string theName;
string the Address;
list<PhoneNumber> thePhones;
int theNum;
};
ABEntry::ABEntry(string& name, string& address, list<PhoneNumber>& phones)
{
theName = name; //요거는 초기화가아니라 대입임..
theAdress = adress;
thePhones = phones;
theNum = 0;
}
c++ 규칙에 의하면 객체는 생성자본문이 실행되기전에 초기화를 해야한다고 명기되어 있다.
위 코드는 이미 초기화를 진행 후, 생성자에 본문에서 대입이 이뤄졌다.
theNum은 초기화가 되었을까? --> 기본타입은 초기화 보장이 되지 않는다.
따라서 초기화를 명시적으로하는방법은 다음과 같다.
ABEntry::ABEntry(string& name, string& address, list<PhoneNumber>& phones)
: theName(name),
theAdress(adress),
thePhones(phones),
theNum(0)
{}
위위 코드(대입)는 생성자호출 + 복사대입연산자를 연달아 호출
위 코드(초기화)는 복사생성자호출 ( 즉, 초기화리스트는 복사생성자를 호출한다.)
(매개변수 없는 애들은 공란으로 초기화하자.. 알아서 생성자 호출함 ctor)
2. 초기화리스트를 의무적으로 사용하는경우
1. 상수
2. 참조자
이유? 상수와 참조자는 대입연산자를 사용할수 없다.
3. 생성자마다 주렁주렁 달려있는 초기화리스트를 예쁘게하고싶다면?
대입연산자로 초기화가능한 데이터멤버들을 모아서 함수화한다.
4. 초기화순서?
기본클래스 -> 파생클래스 호출 순
데이터 멤버는 선언순서이므로, 선언과 초기화리스트 순서를 일치시켜야 한다.
5. 정적객체
정의 : 자신의 생성된 시점부터 프로그램이 끝날때까지 살아 있는 객체
(1) 전역객체
(2) namespace 유효범위에서 정의된 객체
(3) 클래스 안 static 객체
(4) 함수 안 static 객체
(5) 파일 유효범위에서 static으로 정의된 객체
--> (3), (4) 는 지역정적객체, 그외는 비지역 정적객체이다.
6. 번역단위?
컴파일단위
7. 비지역 정적객체의 초기화순서
별개의 번역단위에서 비지역 정적객체의 초기화순서는 보장하지 않음.
해결방법?
비지역 정적객체 전용함수를 만들고 함수내에 해당객체를 지역 정적객체로 선언하여 해당 레퍼런스를 반환한다.
왜 이렇게 하는가?
지역정적객체는 함수가 호출되어 객체정의에 닿았을때 초기화가 우선 보장되기 때문이다.
class FileSystem{
public:
...
size_t numDisks() const;
...
};
extern FileSystem tfs;
class Directory{
public:
...
Directory(params);
};
Directory::Directory(params)
{
size_t disks = tfs.numDisk(); // 비지역 정적객체인 tfs의 초기화가 보장안되므로 망한다..
}
(비지역 정적객체 tfs가 사용된 코드)
class FileSystem { … };
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
class Directory{ … };
Directory::Directory(params)
{
size_t disks = tfs().numDisk(); // 호출로 변경
}
(비지역정적객체를 지역정적객체로 변경한코드)
이것만은 잊지 말자!
- 기본타입객체는 직접 초기화해라
- 초기화 리스트 사용해라
- 여러 번역단위에 있는 비지역 정적객체들의 호출 순서문제를 피하여 설계해라.