본문 바로가기
Programming/C++

[C++] const 사용 시 주의점

by 롱일스 2020. 8. 27.
반응형

오늘은 클래스 선언할 때 const를 써서 데이터를 보호하고자 하지만 썻다가 디버깅 지옥에 빠지는 경우를 소개하려고 합니다. 

이전 포스트에서 만보기 예제를 다뤘는데, 그 예제를 이번에도 예로 들어서 설명하고자 합니다! 아래 포스트를 참고해주세요.

2020/08/27 - [Programming/C++] - [C++] 헤더파일(.h) 내용 중복 include 방지 방법

먼저, 만보기 프로그램 작성을 위한 Counter 클래스를 선언합니다.

 ●  Counter.h

#ifndef COUNTER_H_INCLUDED 
#define COUNTER_H_INCLUDED

class Counter {       //클래스 Counter의 선언 
	int value;	      //private 데이터멤버 
public:				  //public 멤버함수 
	void reset() 	  //만보기의 값을 0으로 초기화 
	{ value = 0;}	  
	void count()      //만보기의 값을 1 증가시킴 
	{ ++value;}
	int getValue() const  //만보기의 현재 값을 반환함 
	{return value;}
};
#endif

함수를 클래스 선언문 안에 선언해주면 inline 함수처럼 선언된 효과를 가진다.

getValue( ) const 에서 const를 쓴 이유는 좀 있다가 설명하겠다.

 

●  CountMain.cpp

Counter 클래스를 이용한 만보기 프로그램을 아래와 같이 작성해 보았다.

#include <iostream>
#include "Counter.h"
using namespace std;

int main()
{
	Counter cnt;   //Counter 객체의 정의
	cnt.value = 0; //만보기를 0으로 지움
	cout << "만보기의 현재 값 : " << cnt.value << endl;
	cnt.value++;    //만보기 1 증가시킴
	cnt.value++;
	cout << "만보기의 현재 값 : " << cnt.value << endl;
	return 0;
}

이렇게 작성하면 안된다. 오류가 뜬다. 왜??

●  오류 발생의 이유

클래스 내에서 public으로 지정해주지 않으면 디폴트로 private으로 설정된다. 

Counter.h에서 데이터 멤버인 value를 우리는 public으로 지정하지 않았기 때문에 private으로 자동 선언되었고, 
그래서 클래스의 멤버함수 내에선 자유롭게 사용 가능하지만, CountMain.cpp의 main함수에서는 접근할 수 없다!!

그래서 cnt.value = 0 , cnt.value 에서 모두 오류가 발생한다. 
이를 방지하기 위해 멤버함수를 이용해야 한다. 

#include <iostream>
#include "Counter.h"
using namespace std;

int main()
{
	Counter cnt;
	cnt.reset();
	cout << "만보기의 현재 값 : " << cnt.getValue() << endl;
	cnt.count();
	cnt.count();
	cout << "만보기의 현재 값 : " << cnt.getValue() << endl;
	return 0;
}

cnt.reset( ); 을 통해서 아래와 같이 cnt 객체의 데이터 멤버인 value값이 0으로 지정된다.

아까  이 안에서 객체의 상태를 바꾸지 않겠다는 의미이다.

Counter.h 에서 아래 구문의 의미를 더 자세히 살펴보자.

	int getValue() const  //만보기의 현재 값을 반환함 
	{return value;}

int getValue( )함수를 호출해도 value의 값이 변하지 않도록 const를 써준 것이다.

당연히 int getValue() const {return value;}에서 안에 value값을 수정하는 ++value; 이런 문장을 쓰면 에러가 발생한다. 

객체를 함수의 매개변수로 전달할 때 const를 쓴다.

또 다른 예를 하나 살펴보자.

●  데이터 멤버의 값을 수정하지 않는 멤버함수

Counter.h를 아래와 같이 정의해보자. getValue 함수 뒤에 const를 뺀 경우다.

/* Counter.h */
class Counter {      
	int value;	      
public:				 
	void reset() 	  
	{ value = 0;}	  
	void count()      
	{ ++value;}
	int getValue() 
	{return value;}
};

 아래 함수 f의 매개변수로 Counter 객체를 받게 되면 문제없이 잘 동작한다. count함수로 값도 쓰고 getValue로 읽고.

void f(Counter& c)
{
    c.count();
    cout << c.getValue();
    ...
}

만약 Counter 객체를 상수 참조형으로 쓰면 c의 값을 읽기만 하겠다는 의미이다.

void g(const Counter& c)
{
    cout << c.getValue();
    ...
}

그런데 이렇게 상수 참조로 객체를 받고 getValue함수로 값을 읽게 되면 getValue함수가 값을 반환만 해주고 실제로 바꾸지 않는 함수인데도 const로 선언되지 않아서 컴파일 오류가 발생한다;;

그래서 아래와 같이 멤버함수에 const를 붙여줘야 한다.

/* Counter.h */
class Counter {      
	int value;	      
public:				 
	void reset() 	  
	{ value = 0;}	  
	void count()      
	{ ++value;}
	int getValue() const
	{return value;}
};

 

728x90
반응형