상세 컨텐츠

본문 제목

C++ 상수 멤버, const

C++/C++98

by deulee 2023. 8. 5. 12:37

본문

상수 멤버

상수 멤버는 한 번 값이 정해지면 변경하는 것이 불가능한 멤버이다. 보통 값이 바뀌면 안되는 중요한 멤버에 상수 멤버로 정의한다.

 

#include <iostream>

class Math
{
private:
	const double pie;
public:
	Math(double p) : pie(p)
	{}
	void getSize(double r)
	{
		std::cout << r * r * pie << std::endl;
	}
};

int main(void)
{
	Math M(3.1416);
	M.getSize(3);
	return 0;
}

상수는 대입이 안되기 때문에 생성자에서 멤버 초기화 리스트를 사용하여 초기화 한 것을 눈여겨 볼만하다.

 

하지만 이렇게 하면 객체가 생성될 때마다 상수에 대한 메모리가 생성되므로 불필요한 메모리 낭비가 생기게 될 것이다.

 

이때 static 키워드를 붙여 정적 멤버로 만드는 것이 가능하다.

 

#include <iostream>

class Math
{
private:
	static const double pie;
public:
	void getSize(double r)
	{
		std::cout << r * r * pie << std::endl;
	}
};

const double Math::pie = 3.1416;

int main(void)
{
	Math M;
	M.getSize(3);
	return 0;
}

 

이렇게 함으로써 객체를 생성할 때마다 초기화 하지 않고 정적 멤버로 선언한 후 딱 한번만 초기화를 하게 된다.

 

이제 이 변수는 정적 상수 멤버가 됨으로써 모든 객체가 공유함과 동시에 변경이 불가능하다.

 

만약 사용하고자 하는 상수가 정수 타입일 경우에는 열거 멤버를 사용할 수도 있다.

 

class Some
{
	public:
		enum { value=123 };
};

 

열거 멤버는 컴파일러가 컴파일중에만 사용하며 실제로 메모리를 차지하지 않으므로 선언문내에서도 값을 정의할 수 있다.

 

상수 멤버 함수

상수 멤버 함수멤버값변경할 수 없는 함수이다.

 

이를 사용하는 방법은 다음과 같다.

 

#include <iostream>

class Point
{
private:
	int x, y;
public:
	Point(int _x, int _y) : x(_x), y(_y)
	{}
	void OutPoint() const // 상수 멤버 함수
	{
		std::cout << x << ' ' << y << std::endl;
	}
	void MoveTo(int x, int y) // 비상수 멤버 함수
	{
		this->x = x;
		this->y = y;
	}
};

int main(void)
{
	Point p1(1, 2);
	p1.MoveTo(5, 6);
	p1.OutPoint();

	const Point p2(1, 2);
	p2.MoveTo(10, 20); // 에러 발생
	p2.OutPoint();
	return 0;
}

 

멤버 함수를 선언할 때 뒤에 const 키워드를 붙여주기만 하면 된다.

 

이렇게 함으로써 이 함수에서 멤버 변수의 값은 변경이 불가능하다.

 

위에서 주목해야 할 점은 하나 더 있다.

 

const로 선언된 상수 객체읽기 전용이라 비상수 멤버 함수(멤버 변경이 가능함)는 호출이 불가능하다는 것이다.

 

즉, 멤버 변수의 변경의 가능성을 일말도 주지 않는다는 것이다. 그러므로 상수로 선언된 객체 상수 멤버 함수만 호출할 수 있다.

 

또한, 상수 멤버 함수가 받는 객체 포인터는 다음과 같다.

 

void func(const Point* const this)

 

즉, this상수이고 this가리키는 대상상수이다.

 

물론 이렇게 한다고 하더라도 컴파일러는 멤버 함수의 코드를 읽어보고 멤버값을 변경하는지 아닌지를 정확히 판단할 수 없다. 즉, 포인터를 통한 간접 변경, 함수 호출을 통한 변경 등 여려 가지 방법을 통해 변경할 방법이 있기 때문에 함수의 내용만으로는 상수성을 정확하게 판단하는 것은 불가능하다.

 

그래서 관례적으로 어떤 멤버 함수가 값을 변경하지 않고 읽기만 한다면 상수 멤버 함수로 지정해주는것이 일반적이다.

 

void func(const Point* ptr);

ptr 객체는 상수이기 때문에 이 객체에 한해서는 상수 멤버 함수만 호출할 수 있다.

 

오버로딩 가능

함수의 상수성은 함수 원형의 일부로 포함된다. 즉, 함수 오버로딩이 가능하다는 것이다.

void func(int a, int b) const;
void func(int a, int b);

즉, 위의 두 함수는 다른 함수로 인식된다.

 

이것이 가능한 이유는 암시적으로 제공되는 인수 this 때문이다. 즉, 다음과 같이 처리되기 때문이다.

void func(const Point* const this, int a, int b);
void func(Point* const this, int a, int b);

이 둘은 엄연히 다르다.

 

컴파일러는 상수 객체에 대해서는 위쪽의 상수 멤버 함수를 호출할 것이고 그렇기 않은 경우는 아래쪽의 비상수 멤버 함수를 호출할 것이다.

 

Mutable

mutable은 상수의 반대 의미로 수정이 가능하다는 의미다.

 

mutable로 지정된 멤버는 상수 함수나 상수 객체에 대해서도 값을 변경할 수 있다. 하지만 잘 쓰이지는 않는다.

 

class Some
{
	private:
		mutable int x;
	public:
		Some(){}
		void func() const { x = 3 };
};

int main(void)
{
	Some S;
	S.func();

	const Some V;
	V.func();
	return 0;
}

이처럼 mutable은 상수성을 완전히 무시해 버린다. 그럼 이것을 왜 만들었을까?

 

이건, 객체의 멤버이면서도 객체의 상태 정보를 나타내지 않는 멤버 변수를 위해서 만들어졌다고 생각하면 된다.

 

즉, 언제든지 수시로 값이 바뀔 수 있는 값을 위해서 만들어졌다.

Mutable 사용하지 않고 상수 멤버 함수에서 멤버 값 변경하기 (비추천)

class A
{
private:
	int x;
public:
	A(int _x) : x(_x)
	{}
	void func() const
	{
		A* ptr = const_cast<A*>(this);
		ptr->x = 10;
	}
	void outNum() const
	{
		std::cout << this->x << std::endl;
	}
};

int main(void)
{
	A a(3);
	a.func();
	a.outNum();
	return 0;
}

 

출처

http://www.soen.kr/lecture/ccpp/cpplec.htm

 

C/C++ 강좌

 

www.soen.kr

https://stackoverflow.com/questions/32686220/is-it-possible-change-value-of-member-variable-within-const-function

 

Is it possible change value of Member variable within "const" function?

Value of constant variable can be changed through pointer tricks, but is it possible to do something like this : class A (){ int x; public: void func () const { //...

stackoverflow.com

 

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

C++ 전역 연산자 함수  (0) 2023.08.05
C++ 연산자 함수  (0) 2023.08.05
C++ 정적 멤버, static  (0) 2023.08.05
C++ this  (0) 2023.08.05
C++ 프렌드  (0) 2023.08.05

관련글 더보기