Taene's

[C++] 동적 크기 배열 구현하기(dynamic_array) 본문

C++ 개념 공부/STL

[C++] 동적 크기 배열 구현하기(dynamic_array)

taene_ 2024. 5. 13. 23:52

- 학생 정보를 관리하는 간단한 응용 프로그램 구현

#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