I'm FanJae.

[c++] const member function, mutable 본문

C++/Basic

[c++] const member function, mutable

FanJae 2024. 8. 17. 17:39

※ 본 포스트는 코드누리 C++ Basic 강의 내용을 보고 정리한 포스트입니다.

 

1. const 객체에서 발생할 수 있는 문제점

#include <iostream>

class Point
{
public:
	int xpos, ypos;

	Point(int x, int y) : xpos{x}, ypos{y} {}

	void set(int x, int y) 
	{
		xpos = x;
		ypos = y;
	}
	void print() 
	{
		std::cout << xpos << ", " << ypos << std::endl;
	}
};
int main()
{
	Point pt(1, 2);

	pt.xpos = 10;   
	pt.set(10, 20); 
	pt.print();    
}

- 테스트를 위해 public에 멤버 데이터를 둔 상태이다. 

- 사실 이 예제는 아무런 문제가 없다. (실행만 생각한다면 말이다.)

- 이때 객체를 const로 만들때 상황을 생각해보면, 아래와 같은 문제가 발생한다.

 

1-1. print()와 같은 값을 바꾸지 않는 함수도 에러 발생

#include <iostream>

class Point
{
public:
	int xpos, ypos;

	Point(int x, int y) : xpos{x}, ypos{y} {}

	void set(int x, int y) 
	{
		xpos = x;
		ypos = y;
	}
	void print() 
	{
		std::cout << xpos << ", " << ypos << std::endl;
	}
};
int main()
{
	const Point pt(1, 2);

	pt.xpos = 10;   // error
	pt.set(10, 20); // error
	pt.print();     // error?
}

- main에서 상수 객체를 선언했을때, 직접 값을 바꾸는 행위나 멤버 함수를 통해 값을 바꾸는 시도를 했다.

- 이는 당연히 에러가 발생해야 하는데 반해, print()도 에러가 발생하는 것을 확인할 수 있다.  

- 왜 이런 문제가 발생할까?

 

- 이러한 이유는 하나의 클래스를 각각 구현과 선언으로 했을때로 생각해야 한다.

Point.h
class Point
{
public:
///
	void print();
}

Point.cpp
void Point::print()
{

}
#include "Point.h"
int main()
{
    const Point p1(1,2);
    
    pt.print(); // error
}

- 컴파일러는 함수의 선언부를 보고 함수 호출 여부를 결정한다. 

- 즉, Point.h만 보고, 함수 호출 여부를 결정하는 것이지. 실제로 Point.cpp의 print()가 어떻게 구현되어있는지 알 수 없다.

- 하지만 이렇다고 하더라도 상수 객체 또한 print()와 같은 함수는 필요하다.


2. 상수 멤버 함수( const member function)

멤버 함수 괄호 () 뒤쪽에 const를 붙이는 문법 

- 함수를 선언과 구현으로 분리할 경우 양쪽에 모두 붙여야 한다.

② 상수 멤버 함수에서는 멤버의 값을 변경하지 않겠다는 약속. 상수 멤버 함수 안에서는 모든 멤버는 상수로 취급된다. 멤버의 값을 변경하는 경우 컴파일 에러가 발생한다.

상수객체는 상수 멤버 함수만 호출 할 수 있다.

#include <iostream>

class Point
{
public:
	int xpos, ypos;

	Point(int x, int y) : xpos{x}, ypos{y} {}

	void set(int x, int y) 
	{
		xpos = x;
		ypos = y;
	}
	void print() const 
	{
    //  xpos = 10; // error
		std::cout << xpos << ", " << ypos << std::endl;
	}
};
int main()
{
	const Point pt(1, 2);

	pt.print();     // ok
}

- 위와 같이 void print() 뒤에 const를 붙여 상수 멤버 함수를 만들 수 있다.

class Rect
{
	int x, y, w, h;
public:
	Rect(int x, int y, int w, int h)
		: x{x}, y{y}, w{w}, h{h} { } 

//	int getArea() { return w * h;} // 틀린 코드..
	int getArea() const { return w * h;}
};

//void foo(Rect r) // call by value : 복사본 오버헤드
void foo(const Rect& r)
{
	int area = r.getArea(); // ????
}

int main()
{
	Rect r(1,1,10,10);  // 상수객체 아님.

	int area = r.getArea(); // ok.

	foo(r);
}

- 이와 같은 형태로 상수 객체를 많이 사용한다.

- 여기서 중요한 부분은 (const Rect& r)과 같이 사용하는 것이 아주 중요하다.

- const 참조 객체를 만들었으니, 이도 상수 객체이다.

- 객체의 상태를 변경하지 않는 모든 멤버 함수는 반드시 상수 멤버로 만들어야 한다. 주로 getter가 많을 탠데 이러한, getter와 같은 멤버 함수는 반드시 상수 멤버 함수로 만들어야 한다.

 

④ 그외 유의 사항

#include <iostream>

class Number
{
	int value;
public:
	Number(int n) : value(n) {}

	int& get()       { return value;} // 1
	int  get() const { return value;} // 2
};
int main()
{
	Number num(10);
	const Number cnum(10);

	cnum.get(); // 2번만 가능
	num.get();  // 1번 호출, 없다면
	            // 2번 호출.

	num.get() = 20;  // ok
	cnum.get() = 20; // error
}

- 동일 이름의 상수 멤버 함수와 비 상수 멤버 함수를 동시에 만드는 것도 가능하다.

- 이때, 상수 객체는 상수 멤버 함수만 호출이 가능하지만, 비상수 객체는 비 상수 객체를 먼저 호출하려고 시도하며, 비 상수 멤버 함수가 없다면 상수 멤버 함수를 호출하려고 한다.


3. Mutable

#include <iostream>

class Rect
{
	int x, y, w, h;
	mutable int cnt = 0;
public:
	Rect(int x, int y, int w, int h)
		: x{x}, y{y}, w{w}, h{h} { } 

	int getArea() const 
	{
//		static int cnt = 0;
		++cnt;
		std::cout << cnt << std::endl;
		return w * h;
	}
};

int main()
{
	Rect r1(1,1,10,10);
	Rect r2(1,1,10,10);

	r1.getArea();
	r2.getArea();
	r2.getArea();
}

- Mutable을 사용하면 const 멤버 함수 내에서도 특정 멤버 변수를 변경할 수 있도록 하는 것이 가능하다.

- 부득이하게 필요한 경우가 있다곤 하지만, const의 의미가 약해질 수 있기 때문에 신중하게 사용을 요한다.

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

[C++] Copy Constructor  (0) 2024.08.19
[C++] this, Reference return  (0) 2024.08.18
[C++] Static Member  (0) 2024.08.16
[C++] Explicit Constructor  (0) 2024.08.14
[C++] Member initializer list, Default member initializer  (0) 2024.08.14
Comments