const 를 사용하는 것으로 특정 값을 불변 시킬 수 있고 컴파일러 및 다른 프로그래머들은 이를 지켜 줄 수 있다.
여기서 const 를 포인터 사용한다면 왼쪽을 기준으로 하고 오른쪽을 본다
const int* // 값만 상수
int* const // 포인터만 상수
그러니
const int operator*(....)
const int* 와 int const * 는 같다.
STL 의 반복자 또한, * 를 기반으로 만들어 졌기 때문에 주소 변경을 원하지 않을시에 const_iterator 를 사용할 수 있다.
const 함수
const operator 를 이용한 예방
const int operator*(..,..)
다음과 같이 반환형을 수정되지 않도록 막으면
if(a*b=c)
와 같이 == 을 = 로 잘못쓰는 실수를 미연에 방지 할 수 있다.
상수 멤버 함수의 사용
상수 멤버 함수를 사용하는데에는 2가지 이유가 있다.
1. 클래스의 인터페이스를 이해가기 좋게 하기 위해서이다.
해당 클래스의 객체가 변경할 수 있는 함수와 그렇지 않은 함수를 보여줄수 있기 때문이다.
2. const 키워드를 사용해 상수 객체를 사용할 수 있게 하자는 것.
코드의 효율을 위해 중요한 부분이다. 이것은 C++ 프로그램의 실행 성능을 높이는 핵심 기법 중 하나가 객체에 대해 전달 시 '상수 객체에 대한 참조자' 로 진행하는 것이기 때문이다.
따라서 const 형태로 객체에 대해 전달되려면 const 멤버 함수가 준비되어 있어야 한다는 것 이다.
중요 : const 를 쓴다 안쓰다 만의 차이로 오버로딩이 가능하다. 굉장히 중요!
멤버 함수가 상수 멤버 라는 것은 어떤 의미일까?
1. 비트수준 상수성
어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야 그 멤버 함수가 const 임을 인정하는 것 ( static 제외 )
따라서 객체를 구성하는 비트 중 그 무엇하나 바꿀 수 없음을 의미한다. 기본적으로 표준 C++ 에서 정의하는 상수성은 비트 단위 일 것 이다.
2.논리적 상수성
다만 위와 같은 비트 수준 상수성은 보장하지 않는 것이 있다. 객체의 멤버가 포인터 일 때 포인터가 가리키는 값에 대한 변화는 비트 수준의 상수화로 발견하지 못한다는 것이다.
이는 포인터가 상수화 되어 있어도 '가리키는 값'은 얼마든지 바꿀 수 있음을 의미한다.
만약 const 함수 내에서 멤버가 변하길 원하면 mutable 을 사용할 수 있다.
mutable 을 변수 앞에 넣어주면 상수 함수 안에서도 해당 멤버는 수정이 가능해진다.
mutable int
const_cast 를 이용한 코드 중복 예방
음 사실 실제 프로그램 개발 시 이렇게 만드는지는 모르겠다.... const_cast 를 이렇게 사용하는 건 처음 봐서
#include <iostream>
#include <string>
class TextBlock {
public:
TextBlock(const std::string& str) : text(str) {}
const char& operator[](std::size_t position) const {
// 상수 버전: 읽기만 가능
return text[position];
}
char& operator[](std::size_t position) {
// 비상수 버전: 수정 가능
return const_cast<char&>(
static_cast<const TextBlock&>(*this)[position]
);
}
private:
std::string text;
};
int main() {
// 1. 비상수 객체: 읽기와 쓰기 가능
TextBlock tb("Hello, World!");
std::cout << tb[0] << std::endl; // 'H' 출력
tb[0] = 'J'; // 'H'를 'J'로 수정
std::cout << tb[0] << std::endl; // 'J' 출력
// 2. 상수 객체: 읽기만 가능
const TextBlock ctb("Hello, World!");
std::cout << ctb[0] << std::endl; // 'H' 출력
// ctb[0] = 'J'; // 컴파일 에러: 상수 객체에서 수정 불가
return 0;
}
책에 있는 내용을 기준으로 디버깅 해봤다. 간단하게 말하면 만약 상수 버전, 비상수 버전의 함수 2개가 오버로딩 된다면 그 안에 들어갈 내용이 많아질수록 중복 되는 내용도 많아지고 컴파일 시간, 유지보수 등등 관리할게 더 많아지니
비상수 객체도 안전하게 상수 함수를 사용할 수 있도록 const_Cast 를 이용한다는 것이다.
1. 중요한 것은 비상수 객체를 상수화 하고 상수 멤버 함수를 호출하여 코드 중복을 줄이기
2. 다음 const_cast 로 다시 비상수화하여 값 변경 이다.
따라서 좋아보이는 코드는 아니지만 만약 코드 중복을 원하지 않는다면 다음과 같은 프로그래밍 지식을 가지고 있어야할 것이다.
그리고 상수 멤버 함수는 비상수 멤버 함수를 호출하지 않는 것이 좋다.
잊지 말 것
1. const 를 붙여 선언하여 컴파일러가 사용상의 에러를 잡는데 도움을 준다
2. 컴파일러에서 비트수준 상수성을 지키지만, 개발자는 논리적 상수성을 사용한 프로그래밍이 필요하다.
3. 상수 비상수를 고려하기 위한 코드중복이 들어가게 된다면 비상수 버전이 상수 버전을 호출 가능하도록 만드는 것이 좋다.
'Cpp' 카테고리의 다른 글
객체 사용 전 객체 초기화 (1) | 2024.12.15 |
---|---|
#define 대신 const, enum, inline 을 생각 할 것 (2) | 2024.12.14 |
C++의 하위 언어 (0) | 2024.12.14 |
바이트 패딩 (0) | 2024.11.18 |
RAII란? (0) | 2024.11.02 |