언어/C++

복사생성자

빨대도둑 2022. 11. 14. 11:37

함수로 인수를 전달하는 방법

  • 값에 의한 호출(call-by-value)
  • 참조에 의함 호출(call-by-reference)
#include <iostream>
using namespace std;

class Circle {
	int radius;
public:
	Circle(int r=0):radius{r}{}
	~Circle(){}
	void setRadius(int r) { radius = r; }
	void print() { cout << "Circle(" << radius << ")" << endl; }
};

void upgrade(Circle a) {
	a.setRadius(20);
}

int main() {
	Circle obj(10);
	upgrade(obj);

	obj.print();
	return 0;
}
  • 객체를 함수로 전달하면 "값에 의한 호출"이 적용되어 main() 함수의 객체 regular의 내용이 upgrade()의 매개변수에 객체 p로 복사된다. 
  • upgrade()의 매개변수 p도 객체이다. 이 객체는 생성될 때 다른 객체의 내용을 복사하여 생성된다. 따라서 일반적인 생성자가 호출되는 것이 아니라 복사 생성자라는 특수한 생성자가 호출된다. 복사생성자는 우리가 만들지 않아도 컴파일러가 기본적인 버전은 만들어서 사용한다. 

 

> 객체의 주소를 & 연산자로 추출하여서 함수로 전달하면 이것은 객체가 아니기 때문에 생성자나 소멸자가 호출되지 않는다. 그리고 객체를 복사하지 않아도 되기 때문에 시간소모도 줄여준다. 함수 안에서 주소를 이용하여 원본 객체의 내용을 조작할 수 있다. 하지만 이것 때문에 의도하치 않은 원본 객체 훼손이 발생할 수 있다. 

void upgrade(Circle a) {
	a.setRadius(20);
}

int main() {
	Circle obj(10);
	upgrade(&obj);

	obj.print();
	return 0;
}

 

> 함수의 객체 반환

#include <iostream>
using namespace std;

class Circle {
	int radius;
public:
	Circle(int r=0):radius{r}{}
	~Circle(){}
	void setRadius(int r) { radius = r; }
	void print() { cout << "Circle(" << radius << ")" << endl; }
};

Circle createCircle() {
	Circle c(10);
	return c;
}

int main() {
	Circle obj;
	obj = createCircle();

	obj.print();
	return 0;
}

 


복사 생성자

복사생성자는 동일한 클래스의 객체를 복사하여 객체를 생성할 때, 사용하는 생성자이다. 객체를 생성할 때는 일반적인 경우에는 구체적인 값들을 넘기지만 다른 객체들을 전달하여 그 객체와 동일한 값으로 객체를 초기화하는 경우도 많다. 

/*만드는 유형*/

Circle(const Circle& other){
	other로 현재 객체를 초기화
}

 

> 같은 종류의 객체로 초기화 하는 경우

Circle obj(obj2);

 

> 객체를 함수에 전달하는 경우

Circle func(Circle obj){
}

 

> 함수가 객체를 반환하는 경우

Circle func(Circle obj){
	Circle tmp;
    
    
    return tmp;
}

- 객체를 생성할 때 다른 객체에서 복사하여 생성하는 경우에 호출되는 생성자를 복사생성자라고 하며 컴파일러는 거의 모든 경우에 사용할 수 있는 기본 복사 생성자를 자동으로 제공한다. 

 


얇은 복사, 깊은 복사

#include <iostream>
using namespace std;

class MyArray {
public:
	int size;
	int* data;

	MyArray(int size) {
		this->size = size;
		data = new int[size];
	}

	~MyArray() {
		if (data != NULL)delete[] this->data;
	}
};

int main() {
	MyArray buffer(10);
	buffer.data[0] = 1;
	{
		MyArray clone = buffer;
	}
	buffer.data[0] = 2;

	return 0;
}

> buffer 객체가 동적 메모리를 사용하려고 하면 오류가 발생한다. 이러한 결과가 나오는 이유는 기본 복사 생성자에서 단순히 멤버의 값을 다른 객체로 복사하였기 때문이다. 

> 실행오류가 발생하는 이유는 지역 변수인 clone이 소멸되면서 이름을 저장한 메모리 공간을 반납하게 되고 이 동일한 공간을 다른 변수 buffer가 사용하려고 했기 때문이다. 이러한 문제를 얇은 복사라고 한다. 

- 이것을 해결하려면 기본 복사 생성자를 사용하지 않고 직접 복사 생성자를 구현하면 된다. 이것을 깊은 복사라고 한다.

#include <iostream>
using namespace std;

class MyArray {
public:
	int size;
	int* data;
	MyArray(int size);
	MyArray(const MyArray& other);
		~MyArray();
};

MyArray::MyArray(int size) :
	this->size = size;
	data = new int[size];
}

MyArray::MyArray(const MyArray& other) {
	this->size = other.size;
	this->data = new int[other.size];
	for (int i = 0; i < size; i++)
		this->data[i] = other.data[i];
}

MyArray::~MyArray() {
	if (data != nullptr)delete[]this->data;
	data = nullptr;
}

 

클래스 안에 객체 포함하기

객체 지향 프로그래밍 언어의 장점은 코드의 재사용이다. 객체 지향에서는 코드를 재사용할 수 있는 2가지 방법이 있다.

  • is-a: 객체 지향 프로그래밍에서 is-a의 개념은 상속을 기반으로 한다. 
  • has-a: has-a는 하나의 객체가 다른 객체를 가지고 있는 관계이다. 

> 객체 지향프로그래밍에서는 하나의 객체 안에 다른 객체가 포함될 수 있다. 이것을 has-a관계라고 한다. 하나의 객체가 다른 객체를 멤버로 가진다는 의미이다. 

#include <iostream>
#include <string>

using namespace std;

class Stock {
	int code, price;
public:
	Stock(double c, double p): code(c), price(p){}
	void print(){
		cout << code << "." << price << "."<<endl;
	}
};

class Name {
	string name;
	Stock codename;

public:
	Name(string n, Stock cn):name(n), codename(cn) {}
	void print() {
		cout << name << ": ";
		codename.print();
		cout << endl;
	}
};

int main() {
	Stock cn{ 3273220,609000 };
	Name n("lg energy solution", cn);
	n.print();
	return 0;
}

 

'언어 > C++' 카테고리의 다른 글

상속  (0) 2022.11.14
포인터  (0) 2022.11.07
생성자와 접근제어  (0) 2022.10.27
클래스와 객체  (0) 2022.10.27
함수  (0) 2022.10.22