const 포인터
변수를 상수로 바꾸기 위해선 두 가지가 필요합니다.
첫 번째는 const 키워드를 변수명 앞에 붙입니다.
두 번째는 변수를 선언 시 초기화해야 됩니다.
const int a; // compile error !! 선언과 동시에 초기화를 해야합니다
a = 10;
그럼, 상수 포인터는 어떻게 선언할까요?
const 키워드를 포인터 기호* 뒤에, 변수명 앞에 붙이고, 초기화합니다.
int a = 0;
int* const pInt = &a;
상수 포인터는 가리키고 있는 메모리 주소를 변경하지 못합니다.
int 변수가 값을 바꾸지 못하는 것과 마찬가집니다.
하지만, 상수 포인터는 가리키고 있는 주소를 바꾸지 못하는 것이지, 가리키고 있는 객체의 내용을 변경하지 못한다는 것이 아니라는 것을 알아둬야 합니다.
int a = 10;
int b = 11;
int* const pInt = &a;
pInt = &b; // Compile Error !! 주소를 변경할 수 없습니다
*pInt = 20; // 값을 변경합니다.
여기까지 읽었을 때, 혼란스러움을 느낄 수도 있습니다.
다음 문장이 더 익숙하니까요.
int a = 0;
const int b = 0;
const int* pInt = &a;
const int* pTest; // ok 선언과 동시에 초기화를 하지 않아도 됩니다
pTest = &a; // ok
pTest = &b; // ok
하지만 위 pInt의 정체는 const int형 포인터입니다.
상수 포인터가 아닌 포인터고, 포인터는 변수입니다.
그렇기 때문에 위의 pTest 포인터에 a의 주소를 넣은 뒤에, b의 주소를 다시 대입할 수 있습니다.
그러나, 상수 int 데이터를 가리키므로 가리키는 데이터의 내용을 변경할 순 없습니다.
int a = 0;
const int* pInt = &a;
*pInt = 100; // error !!
일반적으로, 포인터가 가리키는 주소보다 내용이 변경되지 않도록 장치를 마련하는 것이 중요하기 때문에 실제로도 위의 문장을 더 많이 보게 되지 않을까 생각합니다.
클래스의 const 멤버 함수
클래스의 멤버 함수를 상수로 만들려면 함수 선언 끝부분에 const 키워드를 사용합니다.
그리고, 멤버 함수가 아닌 경우는 이러한 방식으로 함수를 만들 수 없습니다.
상수 멤버 함수의 목적은 클래스의 멤버들과 다른 상수 멤버 함수를 보호하는 데 있습니다.
상수 멤버 내에서는 멤버 변수를 값을 읽을 수는 있지만 변경할 수는 없습니다.
만약 함수 내에서 클래스 멤버의 값을 변경하거나, 상수 멤버 함수가 아닌 함수를 사용하게 되면 오류를 만나게 됩니다.
class CConstTest{
public:
CConstTest() : m_nValue(10) {}
int FuncNoConst(){
m_nValue++; // 멤버 변수를 변경
return m_nValue;
}
int GetValue() const {
return m_nValue;
}
int FuncConst() const{
//m_nValue++; // error !! 멤버 변수를 수정할 수 없음
//return FuncNoConst(); // error !! const 멤버 함수가 아님
return GetValue(); // ok
}
protected:
int m_nValue;
};
위의 예에서 FuncConst 함수가 상수 멤버 함수고, GetValue 함수도 마찬가집니다.
GetValue 함수가 상수 함수이기 때문에 FuncConst 함수는 GetValue 함수를 믿고 쓸 수 있습니다.
그리고, 상수 멤버 함수 내에서 상수 멤버 함수가 아닌 함수를 사용했을 때 나타나는 문제점을 FuncNoConst 함수를 통해서 바로 보실 수 있습니다.
참고로, 클래스의 생성자는 멤버들을 초기화해야 하기 때문에 상수 함수로 만들 수는 없습니다.
const 클래스
이 항목을 읽기 전에 "클래스를 상수로 만든다는 것은 구체적으로 어떻게 한다는 것일까"라는 질문을 해보면 다음의 설명들이 아주 자연스럽게 와닿습니다.
클래스를 상수로 만드는 것은 클래스가 갖고 있는 데이터를 수정할 수 없도록 만드는 것을 말합니다.
우선, 상수 클래스의 모든 멤버를 상수 상태로 만듭니다.
그리고, 멤버 함수도 클래스의 데이터를 수정할 수 없도록 상수 멤버 함수만 호출할 수 있습니다.
이전 항목에서 설명했듯이, 상수 멤버 함수는 함수 내에서 클래스 멤버를 수정할 수 없고, 상수 멤버 함수가 아닌 함수는 호출할 수 없습니다.
아래의 예에서는 상수 멤버 함수를 호출할 때를 제외하고 모두 컴파일 오류가 발생합니다.
#include <string>
using namespace std;
class CConstTest{
public:
CConstTest() : m_nValue(10) {}
int FuncConst() const{
return m_nValue;
}
int FuncNoConst(){
return m_nValue;
}
string m_strPublic;
protected:
int m_nValue;
};
int main(){
const CConstTest cTest;
cTest.FuncConst(); // 상수 멤버 함수 호출은 ok
cTest.m_strPublic = "CONST"; // error!! 멤버 변수를 수정할 수 없다
cTest.FuncNoConst(); // error!! 상수 멤버가 아닌 함수를 사용할 수 없다.
return 0;
}
참고로, 클래스의 상수 멤버 함수와 비상수 멤버 함수는 다른 함수로 취급됩니다.
void PrintClass(){ cout << "Non-Const";}
void PrintClass() const { cout << "Const";}
그럼, PrintClass 함수 호출을 하면 어떤 함수가 실행될까요?
클래스가 상수인 경우 상수 멤버 함수가, 상수 클래스가 아닌 경우 비상수 멤버 함수가 호출됩니다.
#include <iostream>
using namespace std;
class CConstTest{
public:
CConstTest(){}
void PrintClass(){
cout << "Non-Const\n";
}
void PrintClass() const {
cout << "Const\n";
}
};
void Print( const CConstTest& cTest){
cTest.PrintClass();
}
int main(){
CConstTest cTest;
cTest.PrintClass(); // 비상수 멤버 함수 호출
Print(cTest); // 상수 멤버 함수 호출
return 0;
}
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 복사 생성자 (Copy Constructor)와 복사 생략 (0) | 2024.05.10 |
---|---|
[C++] 클래스의 초기화 리스트 (Initializer List)를 사용하는 이유 (0) | 2024.05.07 |
[C++] void 포인터의 개념과 사용법 (0) | 2024.05.01 |
[C++] 함수 포인터 개념 및 사용하는 이유 (0) | 2024.04.30 |
[C++] 포인터로 배열에 접근하기 (1) | 2024.04.30 |