-
[Effective C++] 3. 낌새만 보이면 const를 들이대 보자!C_C++ 프로그래밍/Effective C++ 2019. 8. 29. 16:30
const
const의 가장 큰 장점은 객체의 외부 변경을 불가능하도록소스코드에 작성을 하면 컴파일러가 이를 반드시 지켜준다는 점입니다.즉, 개발자 입장에서 의도치 않은 사용을 금지하거나, 그러한 의도를 소스코드에 보일 수 있다는 것 입니다.poiner const
char name[] = "rojae"; // 문자열 선언char *p = name; // 비상수 데이터, 비상수 포인터const char* p = name; // 상수 데이터, 비상수 포인터char* const p = name; // 비상수 데이터, 상수 포인터const char* const p = name; // 상수 데이터, 상수 포인터상수라는 단어가 들어가면 외부에서 변경이 불가능하다는 것입니다.또한 void f1(const Widget *pw);void f1(Widget const *pw)두 문장 모두 똑같은 의미를 가지며이는 * (포인터)의 위치의 전과 후에 따라서상수가 어느 부분에 속하는 알 수 있게 됩니다.간단한 예시를 첨부하겠습니다.12345678910111213141516171819202122#include<iostream>int main(){int b = 9;const int *a = &b; // 비상수 포인터, 상수 데이터// 포인터가 가르치는 대상이 상수std::cout << "a :" << *a<< " b :" << b;int c = 9;int * const d = &c; // 상수 포인터, 비상수 데이터// 포인터 자체가 상수std::cout << " c :" << c<< " d :" << *d;//*a = c; // 상수 데이터이기 때문에 값 변경 불가*d = b;return 0;}cs 상수 멤버 함수
const는 멤버함수에도 붙을 수 있는데요.
" 이는 해당 멤버 함수가 상수 객체에 대해서 호출될 함수이다. " 라는 뜻입니다.
12345678910111213141516171819202122232425262728293031323334353637383940#include<iostream>class TextBook{public:TextBook(std::string s){ text.assign(s); }// 상수 멤버 함수를 통해 const로 지정const std::string& operator[](std::size_t position) const{ return text; }// 일반적인 객체 지정std::string& operator[](std::size_t position){ return text; }void print(){std::cout << text << '\n';}void print() const{std::cout << text << '\n';}private:std::string text;};int main(){TextBook tb("Hello");std::cout << tb[0] << '\n';const TextBook ctb("Hello");std::cout << ctb[0] << '\n';tb[0] = 'J';//ctb[0] = 'J'; // const TextBook에 대해서 읽기만 가능하다.tb.print();ctb.print();return 0;}cs 실행시켜보면 const가 붙은 객체는
const에 따라서만 실행되는 것을 알 수 있습니다.
또한 이 부분에서
1std::string& operator[](std::size_t position)cs std::string&을 쓴 이유는 반환되어 값을 정정할 때
복사된 값이 아닌 참조되는 값을 수정하기 위함입니다.
그렇다면 상수 멤버 함수에서 변경하고 싶은 데이터는?
C++의 비기. mutable을 사용하면 됩니다.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include<iostream>class TextBook{public:TextBook(std::string s){ text.assign(s); }// 상수 멤버 함수를 통해 const로 지정const char& operator[](std::size_t position) const{ return text[position]; }// 일반적인 객체 지정char& operator[](std::size_t position){ return text[position]; }void print(){std::cout << text << '\n';}void print() const{std::cout << text << '\n';}std::size_t length() const;private:std::string text;// mutable로 선언된 데이터들은 상수 멤버 함수안에서도 수정이 가능하다 (어느 순간이든).mutable bool lengthIsValid;mutable std::size_t textlength;};std::size_t TextBook::length() const{if(!lengthIsValid){textlength = text.size();lengthIsValid = true;}return textlength;}int main(){TextBook tb("Hello");std::cout << tb[0] << '\n';const TextBook ctb("Hello");std::cout << ctb[0] << '\n';tb[0] = 'J';//ctb[0] = 'J'; // const TextBook에 대해서 읽기만 가능하다.tb.print();std::cout << tb.length() << '\n';ctb.print();std::cout << ctb.length() << '\n';return 0;}cs 위의 예제 소스 사이에 private을 보면
123// mutable로 선언된 데이터들은 상수 멤버 함수안에서도 수정이 가능하다 (어느 순간이든).mutable bool lengthIsValid;mutable std::size_t textlength;cs 이러한 소스가 있고 이 변수들은
1234567std::size_t TextBook::length() const{if(!lengthIsValid){textlength = text.size();lengthIsValid = true;}return textlength;}cs 멤버함수 내에서 (심지어 상수 멤버 함수 안에서도 조차)
수정이 가능합니다.
하지만 상수 멤버 함수와 그냥 멤버 함수와 겹치는 부분은..?
맞습니다.
함수가 겹치는 부분이 많아지게 되고
이는 소스코드 길이가 길어지고 낭비가 되는데요.
이 부분을 수정해보았습니다.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465#include<iostream>class TextBook{public:TextBook(std::string s){ text.assign(s); }// 상수 멤버 함수를 통해 const로 지정const char& operator[](std::size_t position) const{// do something..return text[position];}// 일반적인 객체 지정char& operator[](std::size_t position){// static_cast를 통해서 형 변환 이후// const char& operator[](size_t position) const를 통하여 작업 실행// 이후 const_cast를 통해서 const를 제거한다.return const_cast<char &> ( static_cast<const TextBook&> (*this)[position]);}void print(){std::cout << text << '\n';}void print() const{std::cout << text << '\n';}std::size_t length() const;private:std::string text;// mutable로 선언된 데이터들은 상수 멤버 함수안에서도 수정이 가능하다 (어느 순간이든).mutable bool lengthIsValid;mutable std::size_t textlength;};std::size_t TextBook::length() const{if(!lengthIsValid){textlength = text.size();lengthIsValid = true;}return textlength;}int main(){TextBook tb("Hello");std::cout << tb[0] << '\n';const TextBook ctb("Hello");std::cout << ctb[0] << '\n';tb[0] = 'J';//ctb[0] = 'J'; // const TextBook에 대해서 읽기만 가능하다.tb.print();std::cout << tb.length() << '\n';ctb.print();std::cout << ctb.length() << '\n';return 0;}cs 자 보면 여기가 달라졌네요.
12345678char& operator[](std::size_t position){// static_cast를 통해서 형 변환 이후// const char& operator[](size_t position) const를 통하여 작업 실행// 이후 const_cast를 통해서 const를 제거한다.}return const_cast<char &> ( static_cast<const TextBook&> (*this)[position]);}cs 우선 std::string &과 char&은 제가 임의 상 작성을 하였기 때문에 무시합시다.
return const_cast<char &> ( static_cast<const TextBook&> (*this)[position]);보면 const_cast<char &>로( static_cast<const TextBook&> (*this)[position]); 의 결과 값을캐스팅하여 char &로 변환해줍니다.이때 ( static_cast<const TextBook&> (*this)[position])는(const TextBook&)형 변환을 한(*this) => 현재 객체의 정보가 들어간 포인터입니다.즉, 상수 멤버 함수를 통해서 동작을 하지만마지막에 const char&가 아닌 char&로 형 변환을 해주는 것을알 수 있습니다.마무리
어렵당
※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다
반응형'C_C++ 프로그래밍 > Effective C++' 카테고리의 다른 글
[Effective C++] 5. C++이 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) 2019.09.05 [Effective C++] 4. 객체를 사용하기 전에 반드시 객체를 초기화하자 (0) 2019.09.03 [Effective C++] 2. #define을 쓰려거든 const enum, inline을 떠올리자. (0) 2019.08.28 [Effective C++] 1. C++를 언어들의 연합체로 바라보는 안목은 필수 (0) 2019.08.26