함수로 인수를 전달하는 방법
- 값에 의한 호출(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;
}