1장 C++에 왔으면 C++의 법을 따릅시다 🏳
Effective C++ 책을 읽고 정리하고 합니다.
궁금한 점, 보안점 남겨주시면 성실히 답변하겠습니다. 😁
+ 감상평 댓글로 남겨주시면 힘이됩니다. 🙇
의미적인 제약 const
const
의 가장 아름다운 점은 소스코드 수준에서 사용 과 컴파일러가 해당 제약을
지켜줌 두 가지 일 것이다. 프로그래머가 지정한 제약 수준을 컴파일러가
지켜준다 점 자체가 정말 프로그래밍 언어로 컴퓨터와 대화하는 느낌이니까.. 😌
const
는 정말 다방면에서 많이 사용된다. 클래스 바깥에서는 “전역” 혹은 “네임스페이스” 유효범위를 상수로 정의 및 선언을 하는데 사용할 수 있다. 클래수 내부의 경우, “정적멤버” 및 “비정적 멤버” 모두를 상수로 선언 할 수 있다.
포인터가 조금 까다로울 수 있는데, 포인터는 가리키는 대상을 상수화 할 수 있고, 포인터 자체를 상수화 할 수 있고, 둘 다 상수화가 가능하다.
char greeting [] = "Hello";
char halo [] = "halo";
char *p = greeting; // 비상수 포인터, 비상수 데이터
const char *p = greeting; // 비상수 포인터, 상수 데이터
// 포인터가 가리키는 대상 변화 불가능
char * const p = greeting; // 상수 포인터, 비상수 데이터
// 포인터 자체 자체 변화 불가능
const char * const p = greeting; // 상수 포인터, 상수 데이터
// 포인터, 가리키는 대상 모두 변화 불가능
추가로 설명하자면, const char *p
로 선언된 케이스에 만약 greeting
의 값을 변경하고자 할 경우 아래와 같은 에러를 만나게 된다.
// 본인이 g++로 컴파일 할 때 만난 에러이다.
error: read-only variable is not assignable
즉, 값을 변경할 수 없다는 에러이다. 하지만, halo
라는 값으로 pointer를 변경할
경우 잘 동작한다. 이와 반대로 아래의 char * const p
로 greeting
값 중 일부를 변경하면 잘 동작하지만, 포인터를 halo
변경할 경우 아래와 같은 에러를 만날 것이다.
// 본인이 g++로 컴파일 할 때 만난 에러이다.
error: cannot assign to variable 'p' with const-qualified type 'char *const'
마지막 pointer
와 variable
둘 다에 const
를 붙일 경우 변경은 아무것도
되지않고 컴파일 시 에러를 만날 것이다.
그렇다면 이걸 어떻게 구별할 것인가? (어렵지 않다..🤗)
우리는 * (pointer)
를 기준으로 생각하면 된다. * (pointer)
기준으로 왼쪽에 있을 경우
pointer가 가리키는 대상을 상수화 하는 것이고 오른쪽에 있을 경우 pointer 자체를
상수화 하는 것이다.
항상 * (pointer)
기준으로 생각해야한다. 아래의 예시를 보자.
void f1 (const Widget *pw); // 포인터가 왼쪽에 있다. 따라서 가리키는 대상이 상수
void f2 (Widget const *pw); // f1과 동일
일반적으로 문장을 볼 때, Type
기준으로 보는 경향이 있다. Widget
이 아닌
* (pointer)
기준으로 보자. 현업에서 두 가지다 잘 사용하고 있기 때문에 문장을
만났을 때 햇갈리지 말자!
STL 반복자 const
STL 반복자 는 * (pointer)
를 본뜬 것이다. 어떤 반복자를 const
로 선언하는
것은 포인터 상수화 하는 방법과 동일하다. 반복자 앞에 const를 붙이게 될 경우
가리키는 대상의 상수화 가능하다. 만약 포인터를 상수화 하기 위해서는
const_iterator
로 반복자를 선언해 사용하면 된다.
/*
it 는 const T* 선언과 동일하게 동작한다.
가리키는 대상의 상수화
*/
const std::vector<int>::iterator it = vec.begin();
*it = 10; // 포인터 값을 변경하는 것은 가능.
it++; // 가리키는 대상이 상수화 됐기 때문에 대상의 값 변경 불가능!
/*
const_iterator 사용
포인터를 상수화
*/
const std::vector<int>::const_iterator cIt = vec.begin();
*cIt = 10; // 에러 발생. 포인터 값 변경 불가능
it++; // 가능하다. 포인터가 가리키는 대상에 대해선 값 변경 가능!
const 함수 선언
const
의 가장 강점은 함수 선언 할 때 사용하는 것이다. 함수 선언문에 있어
const
는 “반환값”, “각각의 매개변수” 그리고 “멤버함수” 앞 혹은 전체에 const
성질을 부여할 수도 있다.
🌱 함수의 반환값을 const화 할 경우
함수의 반환 값에 const
를 붙여 줄 경우 안정성이나 효율을 포기하지 않고도
사용자 측에서 에러가 발생하는 상황을 줄일 수 있다. 아래의 예시를 보자.
// 유리수 클래스가 있다고 가정하자.
class Rational { ... };
// 멤버 함수 선언 및 정의
const Rational operator*(const Rational& lhs, const Rational& rhs) {
...
}
int main() {
// 사용자 측면에서 에러를 만나게 됨.
if (a * b = c) ...
}
위와 같이 함수의 반환값을 선언해둘 경우 사용자 입장에서 Rational
이라는
사용할 때 의도치 않는 부분에서 컴파일 시 에러를 먼저 출력해 추후 런타임 버그
가 나는 것을 막을 수 있게해준다.
위의 코드에서 if (a * b = c)
부분을 보자. 사용자는 ==
연산을 통해 a * b
값과 c
값을 비교 하고 싶은데, (야근을 해 정신이 혼미한 상태에서.. 🤢) 잘못쳐
=
하나만 쳤다고 가정해보자. 만약 const
로 선언하지 않았다면, compiler는
사용자가 operator=
를 부르나 보다 하고 그냥 넘어갈 수도 있고, 추후 이상한
버그가 발생해 하루종일 야근을 해야될수도있다..
하지만, const
를 작성할 경우 상수에 =
을 사용한다는 말도안되는 부분을
컴파일러가 미리 사용자에게 에러로 알려줄 것이다.
😴 포스팅 읽는 시간이 너무 길어지면 집중력이 떨어진다. 잠시 숨 좀 고르고 😮💨 다음 포스팅 을 보도록 하자.