C++ Chapter 9.7 : 첨자 연산자 오버로딩
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 9. 연산자 오버로딩 : 첨자 연산자 오버로딩
첨자 연산자 :
[]
추가적인 설명은 이전 포스트인 9.1 연산자 오버로딩 시작하기 참고하기
객체의 배열이나 벡터인 멤버를 객체[index]
로 바로 접근하고 싶을 때 사용하는 연산자 오버로딩이다.
🔔 연산자 오버로딩을 사용하지 않은 경우
#include <iostream>
using namespace std;
class IntList
{
private:
int m_list[10];
public:
void setItem(int index, int value)
{
m_list[index] = value;
}
int getItem(int index)
{
return m_list[index];
}
int * getList()
{
return m_list;
}
};
int main()
{
IntList my_list;
my_list.setItem(3, 1);
cout << my_list.getItem(3) << endl;
my_list.getList()[3] = 1; //포인터로 array를 꺼낸뒤에 값을 바꿔준다.
cout << my_list.getList()[3] << endl;
return 0;
}
IntList 클래스의
private
배열 멤버인 m_list 의 원소에 접근하기 위해선 접근할 수 있는 접근 전용 멤버 함수들을 만들어야 한다. setter, getter.
- void setItem(int index, int value)
- 👉 my_list.setItem(3, 1);
- my_list.m_list[3] = 1 이나 마찬가지.
- (근데
private
멤버라 실제로는 이렇게 못하기 때문에 함수 사용)
- (근데
- my_list.m_list[3] = 1 이나 마찬가지.
- 👉 my_list.setItem(3, 1);
- int getItem(int index)
- 👉 my_list.getItem(3)
- my_list 객체의 m_list[3] 을 리턴 받음
- 👉 my_list.getItem(3)
- int * getList()
- 배열 멤버 my_list 이름을 리턴한다.
- 즉 배열 멤버 my_list 의 포인터를 리턴한다.
- 👉 my_list.getList()[3] = 1;
- my_list.m_list[3] = 1 이나 마찬가지.
- (근데
private
멤버라 실제로는 이렇게 못하기 때문에 함수 사용)
- (근데
- my_list.m_list[3] = 1 이나 마찬가지.
- 👉 * my_list.getList()[3]*
- my_list 객체의 m_list[3] 을 리턴 받음
🔔 [] 연산자 오버로딩 : 멤버 함수로 구현하기
함수를 거치지 않고
객체[index]
만으로도 바로 해당 객체의 배열 멤버에 접근할 수 있도록[]
연산자를 오버로딩 한다.
📢 주의사항 :
[]
연산자 오버로딩은 멤버 함수로만 구현이 가능하다.이유는 모르겠지만😱 전역 함수로 구현하는 것은 막혀있다.
#include <iostream>
using namespace std;
class IntList
{
private:
int m_list[10];
public:
int & operator [] (const int index)
{
return m_list[index];
}
};
int main()
{
IntList my_list;
my_list[3] = 10;
cout << my_list[3] << endl;
return 0;
}
- 피연산자는 2개다.
- IntList 타입의 객체
- 인덱스 (const int)
- 멤버 함수로 구현하므로 인덱스로 쓸 int만 인수 1개 받는다.
- 리턴 타입 👉
int &
참조- my_list[3]
- my_list 객체의 멤버 m_list[3] 값을 참조로 리턴한다.
- 참조로 리턴해야 리턴되는 데이터를 L-value처럼 사용할 수 있다.
- 참조로 리턴해야 my_list[3] = 10; 이런식으로 L-value처럼 사용할 수 있다.
- L-value로 리턴되니 리턴 되는 곳에 바로 값을 대입할 수 있다.
- 참조로 리턴하지 않고 그냥 int로 리턴했다면 리턴 결과값이 임시 공간에 복사되어 그 임시 공간이 R-value로 리턴되기 때문에 my_list[3] = 10; 이런식으로 사용할 수 없다.
- 참고 포스트
- 참조로 리턴해야 my_list[3] = 10; 이런식으로 L-value처럼 사용할 수 있다.
- my_list[3]
🔔 배열 멤버 읽기만 가능하게 만들기 : const
#include <iostream>
using namespace std;
class IntList
{
private:
int m_list[10] = { 1, 2 ,3, 4, 5, 6, 7, 8, 9 , 10 };
public:
int & operator [] (const int index) //입력받는것은 어떤 프로그램이냐에 따라 바뀔 수있음
{
return m_list[index];
}
const int & operator [] (const int index) const // 함수 안에서 멤버의 값을 바꾸지 않겠다는 것을 보장한다.
{
return m_list[index];
}
};
int main()
{
const IntList my_list; // 뒤에 const가 붙은 멤버 함수만 호출한다.
my_list[3] = 10; // 👈💥에러!!💥 수정할 수 없다.
cout << my_list[3] << endl; // 읽기만 가능
return 0;
}
- 리턴 타입 👉
const int &
const int &
은 참조하는 대상의 값을 변경할 수 없다.- my_list[3] = 10;
- 따라서 이렇게 값을 변경하는게 불가능하다.
const
- 멤버의 값을 바꾸지 않는 함수라는게 보장된다.
- 뒤에
const
가 붙으므로const
객체는 이 멤버 함수만 호출할 수 있다.- 따라서 const IntList
my_list
; 객체는const
가 뒤에 붙은 두번째로 오버로딩 된 []연산자를 오버로드 한다.
- 따라서 const IntList
🔔 런타임 에러 방지 : assert
#include <iostream>
#include <cassert>
using namespace std;
class IntList
{
private:
int m_list[10] = { 1, 2 ,3, 4, 5, 6, 7, 8, 9 , 10 };
public:
int & operator [] (const int index)
{
assert(index >= 0);
assert(index < 10);
return m_list[index];
}
const int & operator [] (const int index) const
{
assert(index >= 0);
assert(index < 10);
return m_list[index];
}
};
int main()
{
const IntList my_list;
cout << my_list[3] << endl;
return 0;
}
- 배열 인덱스를 음수 혹은 배열 크기를 넘는 큰 인덱스를
[]
오버로딩의 인수로 넘기지 않도록 미리assert
를 두어 잘못된 인덱스를 넘겼을시 에러를 발생하게 한다. - 더 자세한 내용은 assert 포스트 참고
🔔 포인터 객체를 사용할 때 주의 사항
InList * list = new InList;
list[3] = 10; // 에러
(*list)[3] = 10; // 🙆♂️ 이렇게 써야 한다.
객체를 동적으로 할당 받아 포인터로 참조할 때, list[3]이 아닌 (*list)[3]로 접근해야 한다. 객체[]
로 호출해야하기 때문에!
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기