C++ Chapter 9.7 : 첨자 연산자 오버로딩

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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멤버라 실제로는 이렇게 못하기 때문에 함수 사용)
  • int getItem(int index)
    • 👉 my_list.getItem(3)
      • my_list 객체의 m_list[3] 을 리턴 받음
  • int * getList()
    • 배열 멤버 my_list 이름을 리턴한다.
    • 배열 멤버 my_list 의 포인터를 리턴한다.
    • 👉 my_list.getList()[3] = 1;
      • my_list.m_list[3] = 1 이나 마찬가지.
        • (근데 private멤버라 실제로는 이렇게 못하기 때문에 함수 사용)
    • 👉 * 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개다.
    1. IntList 타입의 객체
    2. 인덱스 (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; 이런식으로 사용할 수 없다.
      • 참고 포스트


🔔 배열 멤버 읽기만 가능하게 만들기 : 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가 뒤에 붙은 두번째로 오버로딩 된 []연산자를 오버로드 한다.


🔔 런타임 에러 방지 : 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]로 접근해야 한다. 객체[] 로 호출해야하기 때문에!



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기


Cpp 카테고리 내 다른 글 보러가기

댓글 남기기