동적 크기 배열 구현하기 / (+) const 함수 / (+) 복사 생성자 vs 복사 대입 연산자

Jin Hur·2022년 3월 22일
0

동적 배열 클래스 소스

#include <string>
#include <algorithm>
#include <iostream>
#include <sstream>
using namespace std;

////////// "동적 배열 클래스" //////////
// 클래스 템플릿
template <typename T>
class dynamic_array {
	T* data;
	size_t n;

public:
	// 매개변수 존재 생성자
	dynamic_array(int n) {
		this->n = n;
		data = new T[n];
	}

	// 복사 생성자
	// for deep copy
	dynamic_array(const dynamic_array<T>& other) {
		cout << "(((((   복사(복제) 생성자 호출   )))))" << endl;
		n = other.n;
		data = new T[n];

		for (int i = 0; i < n; i++) {
			data[i] = other[i];
		}
	}

	// 복사 대입 연산자
	dynamic_array& operator=(const dynamic_array& arr) {
		cout << "(((((   복사(복제) 대입 연산자 호출   )))))" << endl;

		n = arr.n;
		data = new T[n];

		for (int i = 0; i < n; i++) {
			data[i] = arr[i];
		}

		return *this;
	}

	// non-const overloading
	T& operator[](int index) {
		return data[index];
	}
	// const overloading
	const T& operator[](int index) const {
		return data[index];
	}

	// 범위를 체크하는 at 함수
	T& at(int index) {
		if (index < n) {
			return data[index];
		}

		throw "Index out of range";
	}

	// 배열의 크기 반환
	size_t size() const {
		return n;
	}

	// 소멸자에서는 메모리 누수를 방지하기 위해 할당된 메모리를 해제
	~dynamic_array() {
		delete[] data;
	}

	// 반복자 관련 함수
	T* begin() {
		return data;
	}
	const T* begin() const {
		return data;
	}
	T* end() {
		return data + n;
	}
	const T* end() const {
		return data + n;
	}

	// friend 함수로 +연산자 함수 정의 (일반 함수를 통한 연산자 오버로딩)
	/*
	friend 키워드를 사용하여 +연산자 함수가 내 친구라는 것을 컴파일러에 알려줌
	+연산자 함수에서는 dynamic_array 클래스의 private 멤버에 접근 가능
	*/
	friend dynamic_array<T> operator+(const dynamic_array<T>& arr1, const dynamic_array<T>& arr2) {
		// (1) arr1.size() + arr2.size() 크기의 dynamic_array 생성
		dynamic_array<T> result(arr1.size() + arr2.size());

		// (2) arr1 복사
		std::copy(arr1.begin(), arr1.end(), result.begin());

		// (3) 이어서 arr2 복사
		std::copy(arr2.begin(), arr2.end(), result.begin() + arr1.size());

		cout << "+ 연산자 수행 완료" << endl;
		return result;
	}

	// 배열에 저장된 모든 데이터를 문자열로 반환하는 to_string() 멤버 함수
	string to_string(const string& sep = ", ") { // 기본 구분자는 쉼표(,)
		if (n == 0)
			return "";

		ostringstream os;
		os << data[0];

		for (int i = 1; i < n; i++) {
			os << sep << data[i];
		}

		return os.str();
	}

};
///////////////////////////////////////////

////////// 학생 정보를 담을 구조체 //////////
struct student {
	string name;
	int standard;
};
// << 연산자를 이용한 표준 출력 지원
ostream& operator<<(ostream& os, const student& s) {
	return (os << "[" << s.name << ", " << s.standard << "]");
}

///////////////////////////////////////////

// main
int main() {
	int numOfStudent;
	cout << "한 반의 학생수 입력: ";
	cin >> numOfStudent;

	// 동적배열 생성
	dynamic_array<student> arr(numOfStudent);

	for (int i = 0; i < numOfStudent; i++) {
		string name;
		int standard;
		cout << i + 1 << "번째 학생의 이름: ";
		cin >> name;
		cout << "나이: ";
		cin >> standard;

		arr[i] = student{ name, standard };	// 구조체 생성
	}

	// 학생들 출력
	//cout << arr.to_string() << endl;
	for (int i = 0; i < arr.size(); i++) {
		cout << arr[i] << endl;
	}
	cout << endl;

	// 깊은 복사
	cout << "arr = arr2, 깊은 복사" << endl;
	auto arr2 = arr;	// 복사(복제) 생성자 호출
	for (int i = 0; i < arr2.size(); i++) {
		cout << arr2[i] << endl;
	}
	cout << endl;
	// 또는 cout << arr2.to_string() << endl;

	// + 연산자 사용
	auto arr3 = arr + arr2;	// + 연산자 함수 반환 시 복사(복제) 생성자 호출
	cout << "arr3 = arr + arr2, + 연산자 사용" << endl;
	for (int i = 0; i < arr3.size(); i++) {
		cout << arr3[i] << endl;
	}
	cout << endl;
	// 또는 cout << arr3.to_string() << endl;

	// 참고로 아래의 경우 복사 대입 연산자가 호출된다.
	dynamic_array<student> arr4(arr.size());
	arr4 = arr;	// 복사(복제) 대입 연산자 호출
	for (int i = 0; i < arr4.size(); i++) {
		cout << arr4[i] << endl;
	}

	return 0;
}

cf) const 함수

1. 함수명 뒤의 const

void print() const {
	cout << x << ", " << y << endl;
}

멤버변수를 읽기 전용으로 사용하겠다는 표시.

2. 매개변수에서 const

const Point operator+(const Point& rhs) const {
	Point p;
    p.x = this->x + rhs.x;
    p.y = this->y + rhs.y;
    return p;
}

복사 오버헤드 없이 참조하는 변수를 마찬가지로 읽기전용으로 사용하겠다는 표시.

3. 함수 반환타입 앞 const

함수의 반환값을 읽기 전용으로 사용하겠다는 표시.


cf) 복사(복제) 생성자 vs 복사(복제) 대입 연산자

reference: https://pmoncode.tistory.com/23

class Test {
private:    
	int m_i;

public:    

	// 기본 생성자(default constructor)    
    Test(){        
    	this->m_i = 0;        
        cout << "call the default constructor" << endl;
    }    
    
    // 복사 생성자(copy constructor)    
    Test(const Test &t) {        
    	this->m_i = t.m_i;        
        cout << "call the copy constructor" << endl;    
    }    
    // 복사 대입 연산자(copy assignment operator)    
    Test& operator=(const Test &t) {        
    	this->m_i = t.m_i;        
        cout << "call the copy assignment operator" << endl;        
        return *this;    
    }
};



int main() {    
	Test t1;    	// 기본 생성자 호출
    Test t2(t1);    // 복사 생성자 호출
    Test t3 = t1;   // 복사 생성자 호출
    t1 = t2;        // 복사 대입 연산자 호출
    return 0;
}

결과

0개의 댓글