Taene's
[C++] 동적 크기 배열 구현하기(dynamic_array) 본문
- 학생 정보를 관리하는 간단한 응용 프로그램 구현
#include <iostream>
#include <sstream> //ostringstream 사용
#include <string>
#include <algorithm>
template<typename T>
class dynamic_array
{
T* data;
int n;
public:
//배열크기를 인자로 받는 생성자
dynamic_array(int n)
{
this->n = n;
data = new T[n];
}
//복사 생성자
dynamic_array(const dynamic_array<T>& copy)
{
this->n = copy.n;
this->data = new T[n];
for (int i = 0; i < n; i++)
data[i] = copy[i];
}
//멤버 데이터에 접근하기 위한 [] 연산자
T& operator[](int index)
{
return data[index];
}
const T& operator[](int index) const
{
return data[index];
}
//std::array의 at()함수 구현
T& at(int index)
{
if (index < n)
return data[index];
throw "Index out of range";
}
//배열의 크기 반환
int size() const
{
return n;
}
//소멸자
~dynamic_array()
{
delete[] data; //메모리 릭 방지
}
//dynamic_array의 배열 원소를 순회할 때 사용할 반복자 관련 함수 정의
T* begin() { return data; }
const T* begin() const { return data; }
T* end() { return data + n; }
const T* end() const { return data + n; }
//두 배열을 하나로 합치는 연산을 수행하는 + 연산자
friend dynamic_array<T> operator+(const dynamic_array<T>& arr1, dynamic_array<T>& arr2)
{
dynamic_array<T> result(arr1.size() + arr2.size());
std::copy(arr1.begin(), arr1.end(), result.begin());
std::copy(arr2.begin(), arr2.end(), result.begin() + arr1.size());
return result;
}
//배열에 저장된 모든 데이터를 문자열로 반환하는 to_string() 멤버 함수(데이터 구분을 위한 문자열 sep의 기본값은 쉼표임)
std::string to_string(const std::string& sep = ", ")
{
if (n == 0)
return "";
std::ostringstream os;
os << data[0];
for (int i = 1; i < n; i++)
os << sep << data[i];
return os.str();
}
};
//학생 정보를 저장할 구조체 student
struct student
{
std::string name;
int standard;
};
//<<연산자를 이용한 표준 출력
std::ostream& operator<<(std::ostream& os, const student& s)
{
return (os << "[" << s.name << ", " << s.standard << "]");
}
int main()
{
int nStudents;
std::cout << "1반 학생 수를 입력하세요: ";
std::cin >> nStudents;
dynamic_array<student> class1(nStudents);
for (int i = 0; i < nStudents; i++)
{
std::string name;
int standard;
std::cout << i + 1 << "번째 학생 이름과 나이를 입력하세요: ";
std::cin >> name >> standard;
class1[i] = student{ name,standard };
}
//배열 크기보다 큰 인덱스의 학생에게 접근하고 예외처리
try
{
class1.at(nStudents) = student{ "John",8 };
}
catch (...)
{
std::cout << "예외 발생!" << std::endl;
}
//깊은 복사
auto class2 = class1;
std::cout << "1반을 복사해 2반 생성: " << class2.to_string() << std::endl;
//두 학급을 합쳐서 새로운 큰 학급 생성
auto class3 = class1 + class2;
std::cout << "1반과 2반을 합쳐 3반 생성: " << class3.to_string() << std::endl;
return 0;
}
- 이때, dynamic_array 클래스에서 같은 기능을 하는 함수를 2개씩 (하나는 const 없이, 하나는 const 있게) 작성한 걸 볼 수 있다.
- 이것은 객체가 const로 선언됐느냐, 그렇지 않느냐에 따라 사용되는 메서드가 다르기 때문에, const가 없는 버전과 const가 있는 버전을 따로 구현한 것이다.(const가 없는 버전만 구현하면, const로 선언된 객체에서 인덱스 접근과 이터레이터를 사용할 수 없게 된다.)
- 예를 들어 Vec 객체 a와 b를 생성하면
Vec a = { 1, 2, 3 };
const Vec b = { 1, 2, 3 };
객체 a는 const 없이 선언되었으므로 수정이 가능하고, b는 const로 선언되었으므로 수정이 불가능하다.
이때 a는 const가 없으니 원소를 수정할 수 있도록 해야하고, 따라서 const 없이 선언된 메서드와 연결을 시켜야하지만,
b는 const이므로 a와 연결된 메서드와 연결시킬 수 없다. 이유는 a와 연결된 const가 없이 선언된 메서드는 원소를 수정할 수 있는 참조를 그대로 반환하므로 const 규칙을 위배하기 때문이다. 따라서 컴파일러는 b에 대해서는 const가 선언된 메서드와 연결을 시킨다.
a[1] = 0; //a[1]이 참조를 반환하게 하고, 값을 대입할 수 있도록 함.
int temp = b[1]; //b[1]이 const형 참조를 반환하게 함으로써 내용을 수정할 수 없도록 함.
'C++ 개념 공부 > STL' 카테고리의 다른 글
[C++] std::vector (0) | 2024.05.15 |
---|---|
[C++] 파일 입출력(fstream) (0) | 2024.04.02 |
[C++] std::memset (0) | 2024.03.15 |
[C++] std::map (0) | 2023.09.11 |
[C++] std::stack (0) | 2023.09.04 |