가변배열
: 힙 메모리 영역에 동적할당해서 데이터 넣어줬었음
배열 시작주소, 최댓값, 한곗값 필요
클래스 외부에 멤버 공개 안 함 (private)
(💡접근 제한자 명시 안 하면 기본적으로 private)
생성/소멸자는 public
(private이면 컴파일러가 호출 못 함)
동적할당
: C의 malloc, free 대신 -> C++의 new, delete 키워드 사용하기
생성/소멸자는 헤더 파일에 선언하고 cpp 파일에서 정의
-> 클래스 밖에서 구현할 땐 어디에 선언된 멤버 함수인지 지칭해줘야 한다.
CArr::CArr() //생성자
{
}
CArr::~CArr() //소멸자
{
}
-> CArr 클래스 안에 선언된 생성/소멸자 CArr()/~CArr()라는 뜻
멤버 변수 선언했던 순서대로 초기화해 주는 것이 좋다.
만약
int* p = new int;
와 같이 자료형 한 개 크기만큼 할당해 줬다면
delete p;
그냥 이렇게 delete 하나로 해제하면 된다.
-> But 가변 배열은 여러 묶음으로(배열 형태로) 받아왔으니 delete[] 포인터명 형태로 해제한다.
C의 malloc은 내가 요청한 크기만큼 메모리를 동적 할당해서 주소 반환해주고, 그 공간을 포인터로 가리켜 사용하였다.
(할당받은 메모리를 어떤 자료형으로 사용할지는 사용자 마음. 가리키는 포인터 타입에 따라 갈림)
-> 클래스가 들어올 공간이라는 확신이 필요함. C에서는 어느 용도(자료형)로 쓰일지 포인터로 가리켜서 알려줬지만, C++의 클래스에서는 컴파일러가 자동으로 호출할 수 있도록 어떤 자료형으로 사용할지 명시를 해줘야 함.
👉 그래서 malloc은 할당할 크기를 받아갔지만, new는 자료형을 받아감
예시
class CTest
{
private:
int a;
public:
CTest()
: a(10)
{
}
};
CTest* pTest = new CTest;
delete pTest;
int a => 4btye
CTest의 객체 pTest 가 들어올 예정이므로 생성자 호출하여 멤버 초기화pTest에게 반환선언
public:
void push_back(int _Data);
void resize(int _iResizeCount);
정의
void CArr::push_back(int _Data)
{
//힙 영역에 할당한 공간이 다 찬 경우
if (m_iMaxCount <= m_iCount)
{
//재할당
resize(m_iMaxCount * 2);
}
//데이터 추가한 뒤, 후위 연산자로 한계치 1 증가
m_pInt[m_iCount++] = _Data;
}
-> resize(m_iMaxCount * 2);
: 데이터 추가할 때 공간이 부족하면 최대치의 2배로 늘리지만, 재할당 함수를 직접 호출할 때는 확장할 크기를 받아와서 그만큼 재할당한다.
_iDataCount)를 받아온다.선언
public:
void push_back(int _Data);
void resize(int _iResizeCount);
정의
void CArr::resize(int _iResizeCount)
{
// 현재 최대 수용량 보다 더 적은 수치로 확장하려는 경우
if (m_iMaxCount >= _iResizeCount)
{
assert(nullptr);
}
// 1. 리사이즈 시킬 개수만큼 동적할당하기
int* pNew = new int[_iResizeCount];
// 2. 기존 공간 -> 새로운 공간 으로 데이터 복사
for (int i = 0; i < m_iCount; ++i)
{
pNew[i] = m_pInt[i];
}
// 3. 기존 공간은 메모리 해제
delete[] m_pInt;
// 4. 배열이 새로 할당된 공간을 가리키게 한다.
m_pInt = pNew;
// 5. MaxCount 변경점 적용
m_iMaxCount = _iResizeCount;
}
//구조체
tArr arr = {};
InitArr(&arr);
PushBack(&arr, 10);
PushBack(&arr, 20);
PushBack(&arr, 30);
ReleaseArr(&arr);
//클래스
CArr carr;
carr.push_back(10);
carr.push_back(20);
carr.push_back(30);
-> 클래스는 객체 생성될 때 생성자가 자동 호출되면서 함께 초기화됨,
그리고 carr는 지역변수이므로 main함수 종료될 때 소멸자도 호출되니까 직접 할당해제 안 해줘도 됨.
: 구조체로 구현할 때 미흡했던 부분 보완하기
예
CArr carr;
carr.push_back(10);
carr.push_back(20);
carr.push_back(30);
carr[1]; //2번째로 추가했던 데이터(인덱스 1)의 값을 가리키고 싶다
-> 💡 연산자 오버로딩 하기
선언
int operator[] (int idx);
정의
int CArr::operator[](int idx)
{
return m_pInt[idx];
}
carr[1] = 40; 처럼 대입까지 할 순 없을까?
-> 연산자 함수 반환 타입이 int라서 안 됨. 함수의 반환 값은 임시적으로 저장되는 공간에 넣어뒀다가 꺼내온다. (원본이 아님)
지금 arr[1]의 복사본 값을 받아와놓고 거기다 대입을 하려는거
그렇다면 원본에 접근해야 한다.
-> 클래스 CArr의 주소로 접근해야 함
선언(수정)
int* operator[] (int idx);
정의(수정)
int* CArr::operator[](int idx)
{
return m_pInt + idx;
}
활용
int* p = carr[1];
*carr[1] = 100;
-> 💡 해결 : 레퍼런스
👉 참조 변수를 반환해야 한다.
선언
int& operator[] (int idx);
정의
int& CArr::operator[](int idx)
{
return m_pInt[idx];
}
활용
int iData = carr[1];
carr[1] = 100;
-> 컴파일러 동작은 포인터와 똑같지만, 사용자 입장에서는 다이렉트 수정이 가능한 것으로 보임
-> 반환시키고자 하는 변수의 참조를 전달함으로써 , 받아오는 쪽(연산자함수)에서는 반환타입이 곧 원본이 됨