momodudu.zip

#2 C++ const keyword와 오버로딩에 관하여 본문

C++

#2 C++ const keyword와 오버로딩에 관하여

ally10 2019. 10. 1. 00:49

일반적으로 const는 수정이 되는것을 원하지 않을 때 사용하는 keyword임은 흔히 알고들 있는 사실이다. 어려운 keyword는 아니다. 그렇지만 const와 class, overloading이 만나면 개인적으로 정말 심오해진다 (...)

 

함수 선언부에서 사용할 수 있는 const는 아래와같이 세가지 위치가 있다.

const returnType Function(const paramType&) const { ..... }

 

먼저 첫번째 위치의 const 는, returnType에 대한 정의이다. 해당 Function안에서 무언가 작업을 할것이고, 그 작업을 한 결과물에 대해 "임시객체, 혹은 변수"를 리턴을 하게 될틴데, 이 값들이 변경이 되지 않는것을 보장하겠다는 의미이다.

두번째 위치의 const 는 paramter에 대한 정의이다. C++에서 객체를 함수 파라미터로 전달할때, 특히 연산자 오버로딩 할 때는 피연산자인 paramter를 reference로 값을 전달하게 되는데, 이 레퍼런스 값이 변경되지 않는것을 보장하겠다는 의미다.

세번째 위치인 const 는 클래스 내부 멤버함수에서 주로 사용되는데, 함수 내부에서 값을 수정해버리는 일종의 삽질(...)을 방지하기 위한 keyword라고 볼 수 있다. 그래서 이런식으로 keyword가 붙으면 mutable을 제외하고는 클래스 내부의 어떤 변수도 수정할 수 없다.

 

 

즉! const가 어디 붙든지간에, const keyword는 값이 변경되지 않도록 만드는 일종의 안전장치 라고 보면 된다. 아주 clear하게 예를 들어보자면,

class Point
{
private:
	int x;
	int y;

public:
	Point() :x(0), y(0) {}
	~Point() {}

	const Point operator+(const Point& target) const
	{
		Point ret;
		ret.x = this->x + target.x;
		ret.y = this->y + target.y;
		return ret;
	}

	const Point operator+(int var) const
	{
		Point ret = *this;
		ret.x = this->x + var;
		ret.y = this->y + var;

		return ret;
	}

	void setX(int varX) { this->x = varX; }
	void setY(int varY) { this->y = varY; }

	int  getX() const
	{
		return x;
	}

	int	 getY() const
	{
		return y;
	}
};

 

위와 같이 간단한 Point Class가 있고, get/set함수가 있다. 그리고 Point를 위한 연산자 오버로딩을 몇가지 임의로 정의했다.

추가적으로, 생성자와 소멸자는 const keyword를 붙일 수 없다. 기본 전제로 생성자와 소멸자는 객체 변수에 대한 수정을 하기 때문이다. 이제 함수를 하나하나 뜯어보자.

 

const Point operator+(const Point& target) const
{
	Point ret;
	ret.x = this->x + target.x;
	ret.y = this->y + target.y;
	return ret;
}

Point A, B;

Point C = A+B; 와 같은 연산을 위한 오버로딩이다.

const keyword가 위에처럼 3군데에 붙어있는데, 각 const가 의미하는 바는 위에서 설명한것과 같다. function name앞의 const는 A+B의 결과물인 임시 객체가 C로 할당되기까지 변하지 않는다는 의미이며, 파라미터에 붙은 const는 피연산자에 대한 정의이다. 즉 A+B에서 B가 피연산자이므로, B가 변하지 않는것을 보장한다. 마지막으로 함수 뒤 const는 클래스 내부에서 유효한것으로, 객체 A에 대한 정의이다. 즉, this로 통하는 모든 변수는 해당 함수내에서 "수정될 수 없다"라는 의미이다.

수정을 해보려고 해도, 컴파일 에러가 난다.

 

const Point operator+(int var) const
{
	Point ret = *this;
	ret.x = this->x + var;
	ret.y = this->y + var;

	return ret;
}

이건 약간 작위적이지만, 이해를 위해 임의로 만든 다른 + 오버로딩이다.

Point C = A+3; 과 같은 연산에서 A의 x,y에 각각 (x+3, y+3)을 하는 연산자이다.

 

마찬가지로 const keyword를 붙임으로써 안전장치를 넣었다. 여기서 궁금해지는점은, 왜 const int var가 아닐까? 라는 점이다. 우리는 첫번째 +operator overloading에서 Point객체를 레퍼런스로 전달해주었다. 레퍼런스는 상수가 아니다. 그러나 int는 상수다. 만약 우리가 A+3의 연산을 원한다면, 3을 굳이 레퍼런스로 전달하지 않을것이다. 즉 3이라는 상수 자체는 절대로 변하지 않는 constant이므로, const를 붙여도 되고, 안붙여도 된다. 하지만 굳이 reference가 아닌 int에 const를 붙일 필요는 없다.

 

int  getX() const
{
	return x;
}

int	 getY() const
{
	return y;
}

클래스 내부의 get함수는 general하게 생각해보면, 보통 get을 하는 함수 안에서는 함수 값을 변경하는 일이 없어야 한다. 안그러면 함수의 의미가 애매해진다. 누군가 이런 삽질하는것을 방지하기 위해 const 안전장치를 사용한다. 이 안에 class 내부 변수를 수정하고자 한다면 컴파일 에러가 발생한다.

 

마지막으로, 클래스 내부가 아닌곳에 아래와 같은 함수를 정의했다.

const Point operator+(int var, const Point& pnt)
{
	return pnt + var;
}

피연산자가 Point class인 operator+를 정의했다. 즉, Point B = 3+A;와 같은 형식이다.

이때 paramter를 const로 정의했음에 주의하자. 즉, 적어도 이 함수 내부에서 pnt는 "상수" 취급이 된다는 의미이다.

pnt는 변경되어서 안되며, 함수를 타고 들어가면서 변경될 가능성 자체를 배제하기 위해 pnt의 함수 호출에도 제한이 생기게 된다. 즉, const로 선언된 Point는 const Function만을 부를 수 있다.

 

이때 const Function은 아래 세가지 위치에 있던 keyword중 어느 const를 의미하는걸까?

const returnType Function(const paramType&) const { ..... }

 

일단 파라미터로 const를 받는건 아닌것 같고,

const returnType function() { .... }

returnType function() const { ... }

둘중 어떤것을 의미할까?

 

세번째 const를 의미한다. 위에서 세번째 const는 객체 "내부"에서 유효한 것이라고 설명했었다. 실제로 위와 같이 똑같이 짜보고, 상수와 Point class overloading함수에서 뒤에 const를 지웠을땐 컴파일에러가 나고, 앞에 const를 지웠을땐 컴파일 에러가 나지 않는다.

 

이 const는 프로그래머의 실수를 줄여주기 위해 고안된것으로, 생각보다 매우 엄격하다.

아래와 같이 const로 선언을 해서, x값을 직접 바꾸지 않고 x의 reference를 리턴해도 컴파일 에러가 난다.

 

int  getXRef() const
{
	return &x;
}

 실제로 getXRef()안에서 x의 값을 수정한건 아니지만, 이렇게 return을 하게 되는 경우 x의 레퍼런스를 이용해서 간접적으로 수정 될 가능성이 있다. 그래서 이 함수 또한 컴파일 에러 처리된다.

비슷하게 const function 안에서 const가 아닌 method역시 부를 수 없다. 비슷하게 미연에 수정될 가능성을 모두 배제하기 위함이다.

'C++' 카테고리의 다른 글

Generics - template instantiation, specialization  (0) 2022.11.22
#5 constexpr keyword  (0) 2022.08.01
#4 std::uniform_int_distribution  (0) 2022.08.01
#3 Capturing variables in Lambda  (0) 2020.11.25
#1 C++ STL Container  (0) 2019.08.28