Const : 변수를 상수화 하기 위해 사용
쓰이는 위치에 따라서 용도가 조금 달라진다.
- 첫번째, const int* n
: 위와 같이 const가 자료형 뒤에 붙은 경우를 데이터 상수화 라고 하고 n이라는 포인터가 가리키는 대상을 상수화 하겠다는 의미가 된다.
- #include <iostream>
- using namespace std;
- const int*n;
- void main()
- {
- int b =10;
- n = &b;
- b = 20;
- cout<< b<<endl; //허용
- *n = 30; //error (you cannot assign to a variable that is const)
- }
위의 예제를 한번 보자 . n이라는 포인터가 가리키는 곳 B로 지정하고, B의 값은 변경하면 무리 없이 변경되는 것을 볼 수 있지만, *n =30; 이 부분에서는 직접 실행해 보면, 에러가 나는것을 알 수 있다. n이 지닌 값은 변경이 안된다는 것이다. 즉, 실제로 메모리 공간 자체가 상수화 된것이 아니고, n이라는 포인터를 이용해서 데이터 변경을 막겠다는 의미가 된다. (하지만 변수 b를 이용해 변경은 가능하다)
- 두번째, int* const n
: 위와 같이 cosnt가 붙은 경우를 포인터 상수화 라고 한다. 예제를 통해 한번 알아 보자.
- #include <iostream>
- void main()
- {
- int a =20;
- int b =10;
- int* const n = &b;
- n = &a; //you cannot assign to a variable that is const
- }
위에서 같이 선언한 n이라는 포인터가 b를 가르키고 있을 시에, 이런 상황에 변수 a를 또 가리키도록 하려는 것을 허용 하지 않겠다는 의미가 된다.
- #include<iostream>
- using namespace std;
- class Student
- {
- const int id;
- int age;
- char name[20];
- char major[30];
- public:
- Student(int _id, int _age, char* _name, char* _major)
- {
- id=_id; //에러
- age=_age;
- strcpy(name, _name);
- strcpy(major, _major);
- }
- void ShowData()
- {
- cout<<"이름: "<<name<<endl;
- cout<<"나이: "<<age<<endl;
- cout<<"학번: "<<id<<endl;
- cout<<"학과: "<<major<<endl;
- }
- };
- int main()
- {
- Student Kim(200577065, 20, "Kim Gil Dong", "Computer Eng.");
- Student Hong(200512065, 19, "Hong Gil Dong", "Electronics Eng.");
- Kim.ShowData();
- cout<<endl;
- Hong.ShowData();
- return 0;
- }
Student라는 클래스의 id라는 멤버 변수를 const로 선언을 했습니다. 우리 일상생활에서도 보듯이 id라는 것은 고유 숫자로 부여를 받거나 하죠. 하지만 여기 Student 생성자 부분에서 에러를 발생하게 됩니다. 일반적으로 클래스에서의 객체 생성 순서에 제일 처음으로 하는 것은 바로 "메모리 공간의 할당"입니다.
생성자가 호출되지 않는 상태에서, 메모리 할당되는 순간 name, age, id 뭐 이런 것들이 초기화 됩니다. (일단 우리가 원하는 값이 아닌 쓰레기 값으로 채워지게 되죠) 하지만 이렇게 id에 쓰레기 값으로 채워 진다면, const 선언을 했기 때문에 id는 아예 이 쓰레기 값으로 초기화 되어서 두번 다시 바꾸지 못하게 되고, 결국 에러가 납니다.
우리는 위 예제를 통해서 const 멤버 변수는 생성자 내에서 초기화 시킬 수 없음을 알았습니다. 그럼 어떻게 해야 할까요? 이 문제를 해결하기 위한 방법으로 등장한 문법적 방법이 바로 initializer입니다.
바로 생성자의 꺽쇠 안의 몸체 부분이 아니라, 그 사이에 : id(_id) 이런식으로 선언하는 방법이다. (이 부분은 생성자 호출 되기 이전에 완료가 되게 됩니다.) 멤버 변수 id를 Student 생성자 호출될때 인자로 전달되었던 _id로 초기화 해라 라는 의미이다.
initializer에서 const 멤버 변수를 초기화 해라!!!
그럼 클래스의 멤버 함수에 const를 붙인다는것은 어떤 의미를 가질까요? 바로 아래와 같은 의미를 가집니다.
멤버 함수에 const를 붙인다는 것은 멤버 변수의 값의 변경을 허용하지 않음은 물론이고, 멤버 변수값의 변경에 대한 기회도 제공하지 않겠다
const를 쓴 멤버 함수의 간단한 예제를 보겠습니다 .
- #include<iostream>
- using namespace std;
- class Student
- {
- const int id;
- int age;
- char name[20];
- char major[30];
- public:
- Student(int _id, int _age, char* _name, char* _major):id(_id), age(_age)
- {
- strcpy(name, _name);
- strcpy(major, _major);
- }
- void ShowData() const
- {
- //name = "바보"; //에러 발생
- cout<<"이름: "<<name<<endl;
- cout<<"나이: "<<age<<endl;
- cout<<"학번: "<<id<<endl;
- cout<<"학과: "<<major<<endl;
- }
- };
여기서는 Showdata()라는 함수를 const 시켰습니다. 그래서 함수 내에서 만약 주석부분과 같이 값의 변경을 한다고 하면, 에러를 발생하게 됩니다. 더욱 자세한 것은 다음 예제를 통해서 알아 보겠습니다.
- #include <iostream>
- using namespace std;
- class Count
- {
- int cnt;
- public :
- Count() : cnt(0){}
- int* GetPtr() const{
- return &cnt; // Compile Error
- }
- void Increment(){
- cnt++;
- }
- void ShowData() const {
- ShowIntro(); // Compile Error
- cout<<cnt<<endl;
- }
- void ShowIntro() {
- cout<<"현재 count의 값 : "<<endl;
- }
- };
- int main()
- {
- Count count;
- count.Increment();
- count.ShowData();
- return 0;
- }
에러 첫번째, 위의 10번째줄 Compile Error 부분은 멤버 변수 조작은 안하지만, 멤버 변수를 조작할수 있는 기회제공을 하고 있습니다. 여기에서 GetPtr() 함수는 cnt를 포인터를 리턴해 주고 있는데, 만약 누군가가 Getptr을 호출한후 ,cnt의 포인터를 얻어낸다고 한다면, cnt 포인터를 얻어낸 사람은 포인터를 통해 얻은 cnt의 주소값을 통해서 cnt의 값을 조작할 수 있다는 것입니다.
결국, getptr()함수는 간접적으로 멤버 변수의 조작 동기를 제공한 것입니다. 그래서 compile error가 발생합니다. 이 컴파일 에러를 피하기 위해서는 const를 더 추가해주면 됩니다. 바로 아래와 같이 말이죠.
- const int* GetPtr() cosnt
- {
- ............
- }
int* 앞에 const가 붙으면 포인터가 가리키는 곳은 상수화 시키는 데이터 상수화를 하겠다는 거죠. 그래서, 이 포인터를 얻은 영역에서는 cnt라는 변수에 접근은 가능하지만, 변경을 불가능하게 만드는 것입니다.
다음 에러 두번째, 바로 Showdata()함수 안에 Showintro()함수 호출 부분입니다.
- void ShowData() const {
- ShowIntro(); // Compile Error
- cout<<cnt<<endl;
- }
- void ShowIntro() const {
- cout<<"현재 count의 값 : ";
- }
프로그래밍에서 런타임, 컴파일 타임에 결정되는 요소들이 있는데, 값이 변경되느냐는 사항은 런타임에 결정되는 요소이고, 컴파일러는파일 타임에 Showintro 라는 함수를 어디까지 이해하느냐 하면은, 이 함수가 상수화 되지 않는 일반적인 함수라고 인식을 하게 됩니다.
그래서 Showdata() 함수 내부에서 Showintro() 함수 부분을 호출 할때, 컴파일러는 Showintro 함수의 내부를 검사하는 것이 아니라, showintro가 상수화 된 함수인지 아닌 함수인지만 분석해서, 이 함수가 상수화 되어 있으면 멤버 조작할 가능성이 없다고 인식하겠지만, 지금 예제에서는 컴파일러는 showintro가 상수화가 되지 않앗다는 것만 인식하기 때문에 컴파일 에러가 나는 것입니다.
const를 빼는 것도 한 방법이겠지만, 굳이 const를 뺄 필요는 없겠죠? 다음과 같이 Showintro 함수도 const를 붙여 줍니다.
- void Showintro() const
- {
- ..................
- }
그러면 컴파일러가 Showintro가 상수화 되었다고 인식을 하게 되어서 컴파일단에서 에러가 나지 않게 되는 것이죠. 자 완성된 예제입니다.
- #include <iostream>
- using namespace std;
- class Count
- {
- int cnt;
- public :
- Count() : cnt(0){}
- const int* GetPtr() const{
- return &cnt; // Compile Error
- }
- void Increment(){
- cnt++;
- }
- void ShowData() const {
- ShowIntro(); // Compile Error
- cout<<cnt<<endl;
- }
- void ShowIntro() const {
- cout<<"현재 count의 값 : ";
- }
- };
- int main()
- {
- Count count;
- count.Increment();
- count.Increment();
- count.ShowData();
- return 0;
- }
댓글 없음:
댓글 쓰기