본문 바로가기

IT책/Effective c++

4항목 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자

name,age,phone 객체를 초기화 해보겠다

class test  
{ 
private: 
	string name; 
	int age; 
	string phone; 

public: 
	test() {} 
	test(const string& const _name, const int& _age, const string& const _phone) 
	{ 
		name = _name; 
		age = _age; 
		phone = _phone; 
	} 
};

그런데 이런식으로 하면 초기화가 아니라 대입이라고 하더라 (처음알았다)

바로 밑의 방법이 초기화를 한것이라고 한다. 그리고 더욱 효율적인 코드이기도 하다.

 

class test  
{ 
private: 
	string name; 
	int age; 
	string phone; 

public: 
	test() {} 
	test(const string& const _name, const int& _age, const string& const _phone) : name(_name), age(_age),phone(_phone){} 
};

처음의 경우는 name,age,phone의 객체를 한번씩 초기화를 한 후 _name,_age,_phone를 객체안에 대입을 한것이다.

하지만 두번째의 경우는 name,age,phone를 복사 생성자에 의해 _name,_age,_phone를 이용하여 바로 초기화를 한것이다.

 

class test  
{ 
	private: 
	string name;               //1 
	int age;                   //2 
	string phone;              //3 

public: 
	test() : name(),age(),phone() {} 
};

매개변수가 없다면 위의 방법으로도 초기화가 가능하다.

그런데 이 둘의 비용차이는 없다고한다(?) 그런데 왜 하느냐

상수이거나 참조자로 되어있는 멤버는 대입 자체가 불가능하기때문이다 그리고 어떤 데이터 멤버는 초기화 해야하고 어떤것은 안해도 되고 이런거 외우는거 귀찮은 사람은 그냥 초기화 리스트로 초기화를 하는것을 추천한다. 밑져도 본전이니까

 

현장에서 쓰이는 클래스들은 상당수가 여러 개의 생성자를 갖고있다고 한다. 똑같은 멤버데이터 여러개가 초기화 리스트에 곰팡이마냥 덕지덕지 붙어있으면 내 눈이 유감스러워질것같다. 그래서 대입이 가능한 데이터들은 어느 함수 한곳에 다 몰아넣어서 거기서 초기화를 시키는것도 나쁘지않는 방법이라고한다(데이터 멤버의 진짜 초기값을 파일에서 읽어온다든지 데이터베이스에서 찾아오는 경우에 유용하다고한다) 함수 안에서 대입을 이용한 짝퉁보다는 혼모노가 좋다

 

그리고 컴파일시 초기화 순서는 선언된 그대로 라고 한다 위의 코드에 주석으로 순서를 표시하겠다.

그리고 밑의 생성자에서 초기화 리스트의 순서가 바뀌어도 멤버데이터가 선언된 순서대로 초기화된다고한다

순서가 바뀌어도 똑같이 초기화가 되더라도 코드를 보고 혼동하지않도록 선언된 순서대로 초기화를 해주도록하자

 

 

정적 객체(static object)란 자신이 생성되고 프로그램이 종료될때까지 살아있는 객체를 말한다. 스택 객체 및 힙 기반 객체는 애초에 정적객체가 될수없다. 이 놈은 DATA영역에 있다.

정적 객체범주에 들어가는것들은

1.전역객체

2.네임스페이스 유효범위에서 정의된 객체

3.클래스 안에서 static으로 선언된 객체

4. 함수 안에서 static으로 선언된 객체

5.파일 유효범위에서 static으로 정의된 객체 이다

정적 객체중에서도 함수안에있는 정적객체는 지역 정적 객체,나머지는 비지역 정적 객체 라고 불린다

 

번역단위(translateion unit은 컴파일을 통해 하나의 목적 파일(object file)을 만드는 바탕이 되는 소스 코드를 말합니다. 여기서 번역은 소스의 언어를 기계어로 바꾼다는 의미일것이다

 

보통 번역단위는 #include하는 파일을 포함하여 소스파일 하나이다. 그런데 여기서 문제가 서로다른 번역단위의 비지역 정적 객체의 초기화는 순서가 따로 정해져있지 않다는것이다.

 

A라는 번역단위와 B라는 번역단위가 있다고 치자 A는 TEST라는 비지역 정적객체를 B에서 TEST라는 정적객체를 가져왔다 그런데 TEST가 초기화가 된지 안된지 모른다. 그 이유는 아까 말했듯이 서로다른 번역단위에 있는 비지역 정적 객체의 초기화는 순서가 정해져있지않기때문이다.

 

해결방법은

#include <iostream>

class test  
{ 

public: 
	int test_size() {} 

}; 

test& t1()  
{ 
	static test my1; 
	return my1; 
} 
//file1
int size = t1().test_size; 

//file2

 

 

대충 이런식으로 하면된다.

 

이것만은 잊지 말자

*기본제공 타입의 객체는 직접 손으로 초기화한다 경우에 따라 저절로 되기도 하고 안된기도 하기 때문이다

*생성자에서는 데이터 멤버에 대한 대입문을 생성자 본문 내부에 넣는 방법으로 멤버를 초기화 하지 말고 멤버 초기화 리스트를 즐겨 사용한다 그리고 초기화 리스트에 데이터 멤버를 나열할 때는 클래스에 각 데이터 멤버가 선언된 순서와 똑같이 나열한다

*여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계해야 한다 비지역 정적 객체를 지역 정적 객체로 바꾸면된다