※ 복사함수
객체를 복사하는 함수로 복사생성자와 복사대입연산자가 있다.
- 복사생성자 (Copy Constructor): 포인터를 멤버로 갖는 클래스는 깊은 복사(deep copy)를 수행.
- 복사대입연산자 (operator=): 따로 선언하지 않을 경우 default로 얕은 복사를 하는 대입연산자를 컴파일러가 자동생성.
주소 즉, 포인터를 갖는 경우,복사생성자(Copy Constructor)와
대입연산자(Copy Assignment Operator)를 반드시 정의!
#include <iostream>
#include <cstring>
using namespace std;
class Person {
char* name;
char* phone;
int age;
public:
Person();
// VC++에서 char* _name은 문자열 상수를 직접 입력으로 받지 못함
Person(const char _name[], const char _phone[], int _age);
~Person();
Person(const Person& p);
Person& operator=(const Person& p);
void ShowData(); // 선언
};
Person::Person() {
cout << " 매개변수 없는 생성자 호출" << endl;
name = NULL;
phone = NULL;
age = 0;
}
Person::Person(const char _name[], const char _phone[], int _age) {
cout << " 매개변수 3개 생성자 호출" << endl;
name = new char[strlen(_name) + 1]; // NULL문자를 고려하여 +1만큼 할당
strcpy_s(name, strlen(_name) + 1, _name);
phone = new char[strlen(_phone) + 1];
strcpy_s(phone, strlen(_phone) + 1, _phone);
age = _age;
cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
}
Person::~Person() { // DeAllocate the heap
cout << " 소멸자 호출" << endl;
delete[]name;
delete[]phone;
cout << " name 주소 해제 : " << (void*)name << ", phone 주소 해제 : " << (void*)phone << endl;
}
Person::Person(const Person& p) : age(p.age) {
cout << " 복사 생성자 호출" << endl;
name = new char[strlen(p.name) + 1]; // NULL문자를 고려하여 +1만큼 할당
strcpy_s(name, strlen(p.name) + 1, p.name); // 깊은 복사
// The statement name = new char; will create the new heap location
// and then copies the value of obj content to new heap location.
phone = new char[strlen(p.phone) + 1];
strcpy_s(phone, strlen(p.phone) + 1, p.phone);
cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
}
Person& Person::operator=(const Person& p) {
cout << " 복사 대입 연산자 호출" << endl;
if (this != &p) {
delete[]name;
delete[]phone;
name = new char[strlen(p.name) + 1]; // NULL문자를 고려하여 +1만큼 할당
strcpy_s(name, strlen(p.name) + 1, p.name); // 깊은 복사
phone = new char[strlen(p.phone) + 1];
strcpy_s(phone, strlen(p.phone) + 1, p.phone);
age = p.age;
cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
}
return *this;
}
void Person::ShowData() { // 클래스 외부에서 클래스 멤버 함수 정의
cout << " name : " << name << ", phone : " << phone << ", age : " << age << endl;
}
int main() {
cout << endl; // 출력 구분 목적
Person p1("홍길동", "010-1234-5555", 34);
p1.ShowData();
cout << endl;
Person p2(p1); // 복사 연산자 호출
p2.ShowData();
cout << endl;
Person p3;
p3 = p1; // 대입연산자 호출된다. p3.operator=(p1);
p3.ShowData();
// 객체가 소멸되는 순서는 객체가 생성된 순서의 반대다.
cout << endl;
}
출처: https://link2me.tistory.com/1755?category=1075719 [소소한 일상 및 업무TIP 다루기:티스토리]
[Effective C++ _item 5]에서 말했듯, 복사함수는 컴파일러가 생성해 주지만
우리가 객체복사함수를 선언한다는 것은 컴파일러의 복사함수의 기본동작이 마음에 들지 않기 때문이다.
[Effective C++ _item 12]: 객체의 모든 부분을 빠짐없이 복사하자!
Ex-1) 문제되는 점이 없다.
Ex-2) 데이터 멤버 하나를 Custormer에 추가하게 되면서 문제가 발생한다.
=> 바로 복사함수가 완전복사가 아닌, 부분복사가 된다는 점이다!
따라서 이 문제를 해결하기 위해 복사함수는 물론 생성자도 모두 다시 갱신 및 작성해줘야 한다.
이 문제가 가장 문제가 되는 경우가 있다. 바로클래스의 상속이다!
아래의 예시에서 PriorityCustomer에 선언된 데이터 멤버를 모두 복사하고 있는 것은 사실이지만
Customer로 부터 상속된 즉, PriorityCustomer의 복사생성자에는 기본클래스 생성자에 넘길 인자들이 명시되지 않아서 기본 생성자에 의해 초기화가 되어 버린다.
또한, 복사대입 연산자의 경우 기본클래스의 데이터 멤버를 건드릴 시도도 하지 않기에 기본 클래스의 데이터 멤버는 변경되지 않고 그대로 있게 된다.
따라서 아래 빨간색 글씨처럼 파생클래스의 복사함수 안에서 기본클래스의 복사함수를 호출하자!
[Effective C++ _item 11]: operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자!
자기대입이란?
어떤 객체가 자기자신에 대입연산자를 적용하는 것이다.
예를 들어 arr[i] = arr[j]; 라는 문장에서 i와 j값이 같다면, 자기대입문이 되는 것이다.
중복참조 때문에 이러한 현상이 발생하게 되는 것이다
중복참조란?
여러곳에서 하나의 객체를 참조하는 것
operator= 구현 시, 객체가 그 자신에 대입되는 경우를 제대로 처리하는기법
1. 원본 객체와 복사대상 객체의 주소를 비교 (위의 if를 이용한 코드)
2. 예외 안정성에 집중하기 (문장의 순서를 적절히 조정 or swap기법) – item 29 참조
'C | C++ > C++' 카테고리의 다른 글
this.code(9)_ smart pointer & type casting_ part 2. (0) | 2022.10.29 |
---|---|
★this.code(8)_ type casting_ part 1. cast operator (★★★) (0) | 2022.10.29 |
this.code(6)_ operator overloading_part1. (0) | 2022.10.25 |
★★this.code(5)_ 상속, 가상함수(동적바인딩 / 오버라이딩 / 추상클래스), 다형성, 다중상속 (0) | 2022.10.25 |
★this.code(4)_ 복사생성자, 이동생성자, perfect forwarding, 임시객체, explicit, friend, static, mutable (0) | 2022.10.25 |