항목 3. 낌새만 보이면 const를 들이대 보자!
상수성에 대해 알아보기위해 연산자 정의 코드를 살펴보자.
const Complex operator +(const Complex &T) const
{
Complex R;
R.image = image + T.image;
R.real = real + T.real;
return R;
}
1. 인수를 레퍼런스로 넘기는이유
(1) 크기 때문에 비효율적이므로 레퍼런스로 넘기는 것이 유리하다.
(2) 직관적 표현을 위해서
1) 포인터로 넘기는경우 c3 = c1 + &c2 이런 형태가 될 것이다.
2. 인수의 상수성
(1) a+b인 경우, 인수로 전달되는 우변의 값을 변경하는 경우는 전혀 없으며, 단지 읽기만 한다
즉, const 지정자를 붙이는 것이 안전하다.
(2) 안붙인다면?
const complex c2(1.0, 2.0);
c3 = c1 + c2 //불가능함
3. 함수의 상수성
(1) 호출 객체의 상태를 바꾸지 않을 경우는, 원칙에 따라 const 함수로 지정하는 것이 좋다.
이항 연산자들은 객체의 값을 읽기만 할 뿐 객체를 변경하지 않는다.
const int i=4;
int j=3,k;
k=i+j;
(2) 함수의 상수성이 없다면 위 코드가 동작할 수 없다.
(3) 반면 객체의 값을 직접 변경하는 연산자( =, ++ 등) const로 지정해서는 안된다.
4. 리턴 타입의 상수성
int i=3,j=4,k;
k=i+j;
(1) i+j의 결과로 리턴되는 값은 7이라는 정수 상수이지 정수형 변수가 아니다.
만약 i+j가 값을 변경할 수 있는 정수형 변수를 리턴한다면 i+j=5;라는 연산식도 허용되어야 할 것이다.
참고사이트 : http://soen.kr/
이제 effective c++ 로 돌아와보자.
1. const의 면모
(1) const 키워드가 붙은 객체는 외부 변경이 불가능하다.
const char * const p= greeting;
(2) const 키워드가 *왼쪽에 있을경우 포인터가 가리키는 대상이 상수 ( 해당 주소에 들어있는 값 변경이 X)
(3) * 오른쪽에 있을경우 포인터 자체가 상수(포인터의 주소값을 변경할 수 x)
2. 리턴타입의 상수성
( a * b ) = c;
이항 연산자에서 리턴타입이 상수성을 가지지 않는다면 위와 같은 대입이 가능해진다.
3. 상수 멤버 함수
(1) 상수멤버함수가 중요한이유?
1) 해당 클래스로 만들어진 객체를 변경할 수 있는 함수가 무엇인지 알수있음 -> 클래스의 인터페이스를 이해하기
좋게함. (참고 : 상수멤버함수는 해당 클래스의 데이터들을 변경할 수 없다.)
2) c++ 성능을 높이는 reference to const(ex. const &char)를 진행하려면, 상수멤버함수가 준비되어있어야 한다.
(참고 : const 키워드 있고 없고의 차이만 있는 멤버 함수들은 오버로딩이 가능함.)
4. 물리적 상수성 vs 논리적 상수성
(1) 물리적 상수성 : 어떤 멤버함수가 그 객체의 어떤 데이터멤버도 건드리지 않아야 한다. (정상적인 아래 코드)
class CTextBlock{
public :
…
char& operator[](std::size_t position) const
{return pText[position]; }
private :
char *pText;
};
(2) 비트적 상수성은 지켜지나 논리적 상수성이 필요한 코드
const CTextBlock cctb("hello");
char *pc = &cctb[0];
*pc = 'J'; // 비트수준의 상수성은 지켜지나..
(3) 논리적상수성
1) 이러한 황당한 상황을 보완하는 개념 : mutable
private :
char *pText;
mutable std::size_t textlength;
mutable bool lengthIsValid;
//mutable 사용할시, 상수멤버함수에서 데이터멤버를 변경할수있다.
std::size_t Ctextblock::length() const
{
if(!lengthIsValid){
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textlength;
}
이것만은 잊지말자!
1. const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는데 유용하다! (리턴타입, 매개변수, 멤버함수, 객체에 붙일 수 있음)
2. 컴파일러쪽에서보면 비트수준상수성을 지켜야하지만, 우리는 논리적인 상수성을 사용해서 프로그래밍해야함.
3. 상수멤버 및 비상수멤버함수가 서로똑같이 구현된경우, 코드중복을 피하는것이 좋다. -> 비상수버전에서 상수버전을 호출하도록 하자! ==> 추후 업데이트