8.1 객체전달과 참조
'값에 의한 호출'로 객체 전달
- 함수를 호출하는 쪽에서 객체 전달
- 객체 이름만 사용
- 함수의 매개 변수 객체 생성
- 매개 변수 객체 생성
- 매개 변수 객체의 공간이 스택에 할당
- 호출하는 쪽의 객체가 매개 변수 객체에 그대로 복사됨
- 매개 변수 객체의 생성자는 호출되지 않음 (복사 생성자가 실행됨)
- 함수 종료
- 매개 변수 객체의 소멸자 호출
- 값에 의한 호출 시 매개 변수 객체의 생성자가 실행되지 않는 이유는? (복사 생성자 말하는 것이 아님)
- 호출되는 순간의 실인자 객체 상태를 매개 변수 객체에 그대로 전달하기 위함
'값에 의한 호출' 방식으로 함수가 호출되는 과정
매개 변수 객체 c 생성될 때는 복사 생성자가 실행됨
'값에 의한 호출'시에 생성자와 소멸자의 비대칭 실행
매개변수로 받은 c에서 생성자가 실행되지 않고 복사 생성자가 실행됨
함수에 객체 전달 - '주소에 의한 호출'로
함수 호출시 객체의 주소만 전달
- 함수의 매개 변수는 객체에 대한 포인터 변수로 선언
- 함수 호출 시 생성자 소멸자가 실행되지 않는 구조
'주소에 의한 호출'로 함수가 호출되는 과정
객체 치환 및 객체 리턴
객체 치환
- 동일한 클래스 타입의 객체끼리 치환 가능
- 객체의 모든 데이터가 비트 단위로 복사
Circle c1(5);
Circle c2(30);
c1 = c2; // c2 객체를 c1 객체에 비트 단위 복사. c1의 반지름은 30이 됨
- 치환된 두 객체는 현재 내용물만 같을 뿐 독립적인 공간 유지
객체 리턴
Circle getCircle(){
Circle tmp(30);
return tmp; // 객체 tmp 리턴
}
Circle c; // c의 반지름 1
c = getCircle(); // tmp 객체의 복사본이 c에 치환. c의 반지름은 30이 됨
객체 리턴
#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle() { radius = 1; }
Circle(int radius) { this->radius = radius; }
void setRadius(int radius) { this->radius = radius; }
double getArea() { return 3.14 * radius * radius; }
};
Circle getCircle() {
Circle tmp(30);
return tmp; // 객체 tmp을 리턴, tmp 객체의 복사본이 리턴된다.
}
int main() {
Circle c; // 객체가 생성된다. radius=1로 초기화
cout << c.getArea() << endl;
c = getCircle();
// tmp 객체가 c에 복사된다. c의 radius는 30이 된다.
// -> c가 tmp인 것이 아니라 안에 내용을 복사받은 것임
cout << c.getArea() << endl;
}
/* 출력 결과
3.14
2826
*/
객체에 대한 참조
#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle() { radius = 1; }
Circle(int radius) { this->radius = radius; }
void setRadius(int radius) { this->radius = radius; }
double getArea() { return 3.14 * radius * radius; }
};
int main() {
Circle circle;
Circle& refc = circle; // circle 객체에 대한 참조 변수 refc 선언
// 메모리 공간은 같지만, 이름이 여러개. 초기화를 해줘야 한다. 안하면 컴파일 에러 발생
refc.setRadius(10);
cout << refc.getArea() << " " << circle.getArea();
}
8.2 복사 생성자
C++에서 얕은 복사와 깊은 복사
얕은 복사(shallow copy)
- 객체 복사 시, 객체의 멤버를 1:1로 복사
- 객체의 멤버 변수에 동적 메모리가 할당된 경우
- 사본은 원본 객체가 할당 받은 메모리를 공유하는 문제 발생
깊은 복사(deep copy)
- 객체 복사 시, 객체의 멤버를 1:1 대로 복사
- 객체의 멤버 변수에 동적 메모리가 할당된 경우
- 사본은 원본이 가진 메모리 크기 만큼 별도로 동적 할당
- 원본의 동적 메모리에 있는 내용을 사본에 복사
- 완전한 형태의 복사
- 사본과 원본은 메모리를 공유하는 문제 없음
C++에서 객체의 복사
객체 간의 초기화와 대입
같은 클래스의 객체 간에 서로 초기화나 대입이 가능
객체 간의 초기화: 복사 생성자 이용
- 같은 클래스의 다른 객체와 같은 값을 갖도록 초기화
- 클래스의 멤버 변수를 1대1로 초기화
객체 간의 대입: 대입 연산자 이용
- 같은 클래스의 다른 객체의 값을 대입
- 클래스의 멤버 변수를 1대1로 대입
Circle c1(10);
Circle c2 = c1; // ==Circle c2(c1): 복사 생성자를 통한 초기화
Circle c3;
c3 = c1; // 객체 간의 대입
// c1 객체를 c3 객체에 비트 단위 복사
복사 생성자
복사 생성자(copy constructor)란?
- 객체의 복사 생성시 호출되는 특별한 생성자
- 같은 클래스의 객체를 이용해서 초기화하는 생성자
특징
- 한 클래스에 오직 1개만 선언 가능
- 모양: 클래스에 대한 참조 매개 변수를 가지는 독특한 생성자
복사 생성 과정
복사 생성자와 객체 복사
#include <iostream>
using namespace std;
class Circle {
private:
int radius;
public:
Circle() { radius = 1; }
Circle(int radius) { this->radius = radius; }
Circle(Circle& c); // 복사 생성자 선언
double getArea() { return 3.14 * radius * radius; }
};
Circle::Circle(Circle& c) { // 복사 생성자 구현
this->radius = c.radius;
cout << "복사 생성자 실행 radius = " << radius << endl;
}
int main() {
Circle src(30); // src 객체의 보통 생성자 호출
// dest 객체가 생성될 때 Circle(Circle& c)
Circle dest(src); // dest 객체의 복사 생성자 호출
cout << "원본의 면적 = " << src.getArea() << endl;
cout << "사본의 면적 = " << dest.getArea() << endl;
}
/* 출력 결과
복사 생성자 실행 radius = 30
원본의 면적 = 2826
사본의 면적 = 2826
*/
디폴트 복사 생성자
개발자가 클래스에 복사 생성자를 작성해놓지 않으면
- 컴파일러가 자동으로 디폴트 복사 생성자를 만들어서 삽입
디폴트 복사 생성자 사례
얕은 복사 문제가 발생하게 됨
메모리 공유하는데 다른 곳에서 이 객체를 값에 의한 호출을 하면 그 함수가 끝났을 때, 소멸자가 호출되게 되버린다.
얕은 복사 생성자를 이용하여 프로그램이 비정상 종료되는 경우
깊은 복사 생성자를 이용
깊은 복사를 하도록 새로 구현한 복사 생성자
class Person {
// private: name; 이라 가정
Person(const Person& person); // 복사 생성자
};
Person::Person(const Person& person) { // 복사 생성자
this->id = person.id; // id 값 복사
int len = strlen(person.name);// name의 문자 개수
this->name = new char[len + 1]; // name을 위한 공간 핟당
strcpy(this->name, person.name); // name의 문자열 복사
// person.name이 가능한 이유는 복사 생성자여서 같은 클래스 타입이기 때문
}
묵시적 복사 생성에 의해 복사 생성자가 자동 호출되는 경우
void f(Person p) { // 2. 값에 의한 호출로 객체가 전달될 때, person 객체의 복사 생성자 호출
p.changeName(“Kim Young woo");
}
Person g() {
Person mother(2, “Hyun So Bin");
return mother; // 3. 함수에서 객체를 리턴할 때, mother 객체의 복사본 생성. 복사본의 복사 생성자 호출
}
int main() {
Person father(1, “Kim Min Soo");
Person mother = father; // 1. 객체로 초기화하여 객체가 생성될 때, Person 객체의 복사 생성자 호출
f(father);
g();
}
'공부 > C++' 카테고리의 다른 글
10. 상속 (0) | 2024.05.27 |
---|---|
9. friend와 연산자 중복 (0) | 2024.05.16 |
7. 여러가지 객체의 생성방법 (0) | 2024.05.14 |
6. 접근지정자, const 객체, static 멤버 (1) | 2024.04.18 |
5. 생성자와 소멸자 (0) | 2024.04.11 |