IT책/Effective c++

3항목: 낌새만 보이면 const를 들이대 보자

우리엄마1 2019. 6. 20. 18:18
char test[] = "hello" 

char *p = test; 

const char *p = test;    //데이터만 변경불가능 

char const *p = test;    //주소만 변경 불가능 

const char const *p = test;    //둘 다 불가능 

void print(const char* const a,const char* const b ) const 
{ 
//이렇게도 사용하자 
}

 

using namespace std; 
vector vec; 

const vector::iterator iter= vec.begin(); 
//T* const 처럼 작동한다 주소는 변경을 못하지만 데이터는 수정이 가능하다 

vector::const_iterator iter = vec.begin(); 
//const T*  처럼 작동한다 데이터는 수정을 못하지만 주소는 변경이 가능하다 

const vector::const_iterator iter= vec.begin(); 
//const T* const 처럼 작동한다. 둘 다 수정,변경 불가능

 

 

함수의 매개변수 멤버 함수 반환값 그리고 함수 전체에 const 성질을 붙이는게 가능하다

 

멤버함수에 const키워드를 사용하여 사용자가 변경할수있는것과 없는것을 구분하여 가독성을 높여줄수있다.

 

그리고 const가 있냐 없냐에 따라서 오버로딩이 가능하다.

 

using namespace std; 
class test  
{ 
public: 
	test() {} 
	void print(test& _test) { cout << "나는 1번째!" << endl; } //1번째 
	void print(test& _test) const { cout << "나는 2번째!!" << endl; } //2번째 
	void print(const test& _test) { cout << "나는 3번째!!!" << endl; } //3번째 
	void print(const test& _test) const { cout << "나는 4번째!!!!" << endl; } //4번째 
}; 
int _tmain()  
{ 
	test t1,t3; 
	const test t2, t4; 

	t3.print(t1); //1번째 
	t4.print(t1); //2번째 
	t3.print(t2); //3번째 
	t4.print(t2); //4번째 
} 	//출력되는 순서이다



 

 

그리고 아무리 const를 사용하고있다고 해도 이 경우에 문제가 생긴다.

데이터를 변경해버린다는것이다

 

 

using namespace std; 
class test  
{ 
private: 
	char* str; 
public: 
	test(const char* text) 
	{ 
		str = new char[strlen(text) + 1]; 
		memset(str, 0, strlen(text)+1); 
		strcpy(str, text); 
	} 
	char& operator[](int i) const  
	{ 
		return str[i]; 
	} 
}; 
int _tmain()  
{ 
	test str("Test"); 
	char* p = &str[0]; 
	*p = 'B'; 
	cout << p << endl; 
} 

//출력값은 Test가 아닌 Best로 출력이된다

 

조심하자

 

그리고 이제 상수함수와 비상수함수의 코드 중복문제에 대해 알아보겠다 .

 

char& operator[](int i) 
{ 
	//어쩌구저쩌구  
	//이래저래 
	//많이많이 
	return str[i]; 
} 
const char& operator[](int i) const  
{ 
	//어쩌구저쩌구  
	//이래저래 
	//많이많이 
	return str[i]; 
}

 

이런식으로 기능은 매우 똑같으나 상수와 비상수로 나뉘어 이런식으로 중복된 코드를 사용하면 유지보수, 컴파일시간, 코드크기증가

등등.. 에바참치스러운일이 일어날것이다.

그러니 그런일이 일어나지않고 비상수함수에서 상수함수를 불러들이면 된다.

 

char& operator[](int i) 
{ 
	return const_cast<char&>(static_cast(*this)[i]); 
} 

const char& operator[](int i) const 
{ 
	//어쩌구저쩌구  
	//이래저래 
	//많이많이 
	cout << "나 상수멤버함수가 실행되었따!" << endl; 
	return str[i]; 
} 

}; 
int _tmain()  
{ 
	test str("ttt"); 

	str[0]; 

	return 0; 
} 
//실행결과는 "나 상수멤버함수가 실행되었따!" 가 출력될것이다 

//상수함수에서 비상수함수를 역으로 부를수도있다 



const char& operator[](int i) const  
{ 
	//어쩌구저쩌구  
	//이래저래 
	//많이많이 
	return static_cast(const_cast<test&>(*this)[i]); 
} 

char& operator[](int i) 
{ 
	//어쩌구저쩌구  
	//이래저래 
	//많이많이 
	cout << "나 비상수멤버함수가 실행되었따!" << endl; 
	return str[i]; 
} 

}; 
int _tmain()  
{ 
	const test str("ttt"); 

	str[0]; 

	return 0; 
}

 

 

그런데 2번째 방법인 상수함수에서 비상수함수를 부르는짓은 하지말자 상수함수는 컴파일러와 논리적인 상태를 바꾸지않겠다고 약속을 한것이다.

그리고 비상수함수는 상수함수의 반대로 약속을 하지 않았다.

그런데 상수함수에서 비상수함수를 불러버리면 컴파일러에게 뒤통수치는일이나 마찬가지이다. 그러면 사람과 똑같이 컴파일러도 우리를 등질것이다.

우리가 역으로 부르려면 일단 const를 때야된다 이게 재앙의 씨앗이 된다고 한다(책에서)

쨋든 하지말라는건 하지말자

 

그리고 const는매우 좋은거다 마구마구싸질러버리자

 

 

 

이것만은 잊지 말자!

1.const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는 데 도움을 준다

2.컴파일러 쪽에서 보면 비트수준 상수성을 지켜야한다 하지만 우리는 논리적인 상수성을 사용해서 프로그래밍하자(mutable)

3.상수멤버함수와 비상수멤버함수가 기능이 똑같다면 위의 코드를 사용해서 중복을 피하자