2015년 10월 3일 토요일

[C++] const function

const function
: 멤버 함수 내부에서 사용되는 멤버 변수를 상수화하는 함수로 멤버 변수의 값을 변경할 수 없다.



const 키워드를 함수에 사용하게 되면, 함수의 signature 가 변경된다.

const 사용전에는 void display(); 의 signature는 void Point::display(Point* const this); 가 될 것이다.
this 자체의 주소는 변경할 수 없지만, this 포인터로 접근하여 변경할 수 있다.

const 를 사용하면, void Point::display(const Point* const this);로 signature가 변경되기 때문에
this->x 로 접근하여 변경이 불가능하게 된다.


display() 는 멤버 변수를 변경하기 위한 것이 아니라, 단순하게 출력이 목적이다.
멤버 변수 x의 값을 변경하는 동작은 display 함수 목적에 맞지 않다. bug가 된다.
이렇게 함수의 목적과 다르게 멤버 변수를 수정하지 않도록 보장하기 위해서 const function 을 사용하게 된다.

또한 상수 함수는 signature 가 다르기 때문에 overloading 이 가능하다.

상수 함수의 사용 목적 :
객체의 상태 값을 변경하지 않게 하기 위해서, 객체의 안정성을 높인다.
하지만, 그 본질은 안정성 때문이 아니다. 안정성은 장점이지 본질이 아니다.


또한 함수의 인자로 전달되는 원본 객체에 대한 수정을 방지하기 위해 const 와 & 를 사용한다.
display 가 const 가 아닐 경우, foo 함수 내에서 p의 객체 내부를 변경할 수 있게 된다.
상수 객체로 들어온 인자는 상수 함수밖에 호출이 안되기 때문에, const 를 사용하는 것이다.
(안정성 때문에 사용하는 것이 아니라..)

const 를 사용하는 것은 optional 이 아니라 requirement 이다.
상수 객체로 들어온 인자는 상수 함수 밖에 호출이 안되기 때문이다.


void display() const 를 주석 처리하면,
main 함수에서 foo(p); 코드가 실행이 안되는 이유가 바로
상수 객체로 들어온 인자는 상수 함수 밖에 호출이 안되기 때문이다.

상수 함수와 멤버 함수가 overloading 되었을 때, 누가 호출되는가?
1) 일반 객체에서는
일반 멤버 함수가 먼저 호출되고, 이것이 없을 대 상수 함수가 호출된다.
2) 상수 객체에서는
당연히 상수 함수만이 호출될 수 있기 때문에, 일반 멤버 함수는 호출되지 못한다.



util function 으로 to_string() 을 지원하는 class 를 작성했다.
하지만 sprintf 가 매번 호출되는 것이 성능 저하의 원인이라 가정하고, cached 개념을 적용해보자.


to_string()함수는 객체 내부의 x, y의 상태를 변경하는 함수가 아니다.

하지만 const 함수로 변경한다면, 아래 cache_valid, sprintf 코드에서 에러가 발생한다.

그렇다면 const내에서 사용되는 멤버 변수 중에서 상수화를 보장할 필요 없는 것은 mutable 키워드를 사용하면 어떨까?

하지만 mutable 를 사용한다는 것은, 클래스의 설계가 잘못되었다고 볼 수 있다.

mutable 을 사용하길 원하지 않는다면, mutable 대신에 변수를 구조체로 변경하고
그 구조체를 가리키는 포인터를 선언하여, 그 포인터로 const 함수에서 변경하도록 할 수 있다.

하지만 Cache 객체를 동적할당하기 때문에 그에 따른 단점도 발생할 수 있다.


Summary
1. 상수 함수는 멤버 변수의 포인터나 또는 참조를 리턴할 수 없으며, 값의 수정도 불가능하다.
2. 상수 함수 내에서 상수 함수가 아닌 함수를 호출하는 것은 불가능하다.
3. 상수 함수 내에서 멤버 변수의 포인터나 또는 참조를 리턴하고 싶은 경우,
혹은 값을 수정하고 싶은 경우에는 mutable 키워드를 사용한다.
혹은 변수를 구조체로 변경하여 해당 구조체의 포인터를 사용한다.
4. 변하는 것(정책)과 변하지 않는 것(알고리즘)을 분리하는 개념을 적용해도 된다.
5. 일반 함수와 상수 함수가 오버로딩된 경우 호출 순서
일반 객체 : 일반 함수를 먼저 호출하고, 일반 함수가 없을 경우, 상수 함수 호출
상수 객체 : 상수 함수만 호출 가능하다.
6. 상수 함수는 선택이 아니라 필수이다.
상수 객체는 상수 함수만을 호출할 수 있기 때문이다.







댓글 없음:

댓글 쓰기

[C++] meta programing

재귀 호출에 관해 template meta programming 을 적용한 예제를 살펴보자. #include using namespace std; int fact(int n){ if(n factorial 연산을 하는 일반적인 재귀 호출 함...