[C++] 클래스를 이용한 가변 배열
2024. 3. 18. 13:48ㆍProgramming Language/C++
CArr.h
#pragma once
class CArr // (C는 class라는 의미로 사용했다.)
{
private:
int* m_pInt; // 주소값 (m = 멤버라는 의미로 사용했다.)
int m_iCount; // 현재 들어와있는 데이터 개수
int m_iMaxCount; // 최대 개수
public:
void push_back(int _iData); // 가변 배열로 따지면 데이터 추가 함수이다.
public:
CArr(); // 가변 배열로 따지면 => 배열 초기화 함수
~CArr(); // 가변 배열로 따지면 => 배열 메모리 해제 함수
};
CArr.cpp
#include "CArr.h"
// 생성자
CArr::CArr()
: m_pInt(nullptr)
, m_iCount(0)
, m_iMaxCount(2)
{
m_pInt = new int[2]; // int 자료형 2개만큼 할당한다.
}
// 소멸자
CArr::~CArr()
{
delete[] m_pInt;
}
Arr.h
#pragma once
// 가변 배열 자료형 tArr (int)
typedef struct _tagArr
{
int* pInt;
int iCount;
int iMaxCount;
}tArr;
// 배열 초기화(초기값을 줄뿐 문법적으로 진짜 초기화는 아니다.) 함수
void InitArr(tArr* _pArr);
// 데이터 추가 함수
void PushBack(tArr* _pArr, int _iData);
// 공간 추가 확장 함수
void Reallocate(tArr* _pArr);
// 배열 메모리 해제 함수
void ReleaseArr(tArr* _pArr);
Arr.cpp
#include <iostream>
#include "Arr.h"
// 배열 초기화
void InitArr(tArr* _pArr)
{
_pArr->pInt = (int*)malloc(sizeof(int) * 2);
_pArr->iCount = 0;
_pArr->iMaxCount = 2;
}
// 공간 추가 확장
void Reallocate(tArr* _pArr)
{
// 1. 2배 더 큰 공간을 동적할당한다.
int* pNew = (int*)malloc(_pArr->iMaxCount * sizeof(int) * 2);
// 2. 기존 공간에 있던 데이터들을 새로 할당한 공간으로 복사시킨다.
for (int i = 0; i < _pArr->iCount; ++i)
{
pNew[i] = _pArr->pInt[i];
}
// 3. 기존 공간은 메모리 해제
free(_pArr->pInt);
// 4. 배열이 새로 할당된 공간을 가리키게 한다.
_pArr->pInt = pNew;
// 5. iMaxCount 변경점 적용
_pArr->iMaxCount *= 2;
}
// 뒤에 데이터 추가
void PushBack(tArr* _pArr, int _iData)
{
if (_pArr->iMaxCount <= _pArr->iCount)
{
// 재할당
Reallocate(_pArr);
}
// 데이터 추가
_pArr->pInt[_pArr->iCount++] = _iData;
}
// 배열 메모리 해제
void ReleaseArr(tArr* _pArr)
{
free(_pArr->pInt);
_pArr->iCount = 0;
_pArr->iMaxCount = 0;
}
main.cpp
#include <iostream>
#include "Arr.h"
#include "CArr.h"
class CTest
{
private:
int a;
public:
CTest()
: a(10)
{
}
};
int main()
{
// 클래스 동적 할당 예시
CTest* pTest = new CTest; // a = 10
delete pTest; // pTest의 타입이 CTest*라는 것을 확인한다.
// 1. 가변 배열 예시
tArr arr = {}; // 클래스 배열 초기화
InitArr(&arr); // 초기값 호출
// 값 넣기
PushBack(&arr, 10);
PushBack(&arr, 20);
PushBack(&arr, 30);
// 메모리 해제
ReleaseArr(&arr);
// 2. CArr 예시
CArr carr; // 가변 배열에서 InitArr(&arr);까지 포함되어있다.
carr.push_back(10);
carr.push_back(20);
carr.push_back(30);
// carr은 지역변수이기 때문에 소멸은 신경쓰지 않아도 된다.
// CArr 예시의 경우 C에서 제공해주는 기본 문법 배열은 아니다.
// 내가 만든 배열도 carr[1];로 2번째로 넣었던 20을 지칭하고 값을 변경하는 것이 가능했으면 좋겠다.
//carr[1] = 40; // 오류 => 애초에 배열(주소)가 아니라 객체이다.
// operator 함수를 이용해서 만들면 된다.
int iData = carr[1];
//carr[1] = 200; // 오류 => 반환 타입이 int이다.
// 함수의 반환 타입은 호출된 함수와 함수끼리 이 함수가 종료될 때 반환한 값을 임시적으로 저장되어있는 곳에서부터 값을 넣어놓고 꺼내온다.
// operator[] 함수가 종료되고나서 돌아올 때 CArr 객체의 m_pInt가 가르키고 있는 곳에 index 1 즉, 두 번째 데이터를 갖고와서 200을 넣어주려고 한다.
// 하지만 현재 상황은 갖고 있던 값을 임시 공간에 넣어놓고 그 복사본을 받아온 것이다.
// 임시 공간에 200을 넣어봤자 수정할 수도 없고 진짜 내가 원하는 곳을 수정하는 것이 아니다.
// operator[] 함수는 반환 타입이 int이기 때문이다.
// m_pInt[idx]에 들어있던 값을 전달시키고 main() 쪽에서 꺼내온 것뿐이다.
// 원본 그 자체가 아니다.
// 원본 그 자체에 접근할 수 있는 방법 중 해당 변수의 주소를 알려줘야 하는 방법이 있다.
// int에서 int* 타입으로 바꿔보자.
// 그럼 주소이기 때문에 역으로 접근하여 주소로 가볼 수 있다.
// 애초에 []는 주소 연산이기 때문에 원본으로 접근하는 부분을 없애면 그것이 바로 주소이다.
// 시작으로부터 몇 칸 떨어진 곳의 주소값 그 자체를 바로 반환한다.
// *(m_pInt + idx); → m_pInt + idx;
int* p = carr[1];
*carr[1] = 100;
// 원래 배열 문법이랑 전혀 관련이 없다.
// 진짜 배열은 아니지만 마치 배열과 같은 기호로 인덱스로 접근해서 값을 수정해볼 수 있다.
// 나는 인덱스 1로 꺼내온 것이 값이고 다이렉트로 수정하고 싶은데, 이 과정들이 가능하려면 c++에서 제공하는 래퍼런스를 사용해야 한다.
// 즉, 반환 타입이 int*가 아닌 int&여야 한다.
// 반환하는 곳을 그대로 참조를 전달하면 반환되는 것이 곧 idx와 동일시되는 것이다.
// 그럼 operator[] 함수에서는 공간 그 자체를 주면 된다.
// 호출시킨 operator[] 함수에서 반환하겠다고 한 반환 타입이 곧 idx와 동일시됐기 때문에 idx를 수정하는 것이 곧 반환하려는 것을 수정하는 것과 같다.
// 개념 자체는 주소를 통해서 접근해서 수정하는 것이지만 이는 컴파일러가 알아서 해준다.
// 덕분에 원하는 형태가 나올 수 있던 것이다.
int iData = carr[1];
carr[1] = 100;
// carr은 배열이 아닌데 배열처럼 사용할 수 있고, 접근해서 값도 확인하고 수정할 수 있게 됐다.
return 0;
}
'Programming Language > C++' 카테고리의 다른 글
[C++] 범위 지정 연산자(::), using namespace std; (0) | 2024.03.19 |
---|---|
[C++] 구조체 · 클래스 템플릿으로 리스트 구현 (0) | 2024.03.18 |
[C++] 클래스 (+ 멤버 함수, this 포인터) (0) | 2024.03.18 |
[C++] 정렬과 버블 정렬 구현, 함수 포인터 (0) | 2024.03.17 |
[C/C++] 연결 리스트 구현 (0) | 2024.03.17 |