-
[C++] 복사생성자(copy constructor)C++ 2022. 12. 27. 17:22
C++의 객체 생성과 동시에 초기화하는 방식은 두 가지가 있음
(일반자료형도 해당되지만 객체를 대상으로 말하겠다)1. 객체 간 대입연산
SoSimple sim1 = sim2;
2. 복사생성자
SoSimple sim1(sim2);
그리고 이 둘은 똑같다
1번으로 쓰면 2번으로 변경되어 실행된다
class Human { private: int NTRP; public: Human(int NTRP) : NTRP(NTRP) { // empty } void showNTRP() { cout << NTRP << endl; } };
[1. 객체 간 대입연산]
int main() { Human Federer(7); Federer.showNTRP(); Human jhmin = Federer; jhmin.showNTRP(); return 0; }
객체 간 대입연산이 실행되었을때 이 코드는 2번(복사생성자) 으로 묵시적 변환이 일어남Human jhmin = Federer; -> Human jhmin(Federer);
하지만, 위 Human 클래스에서는 "객체를 인자로 받는 생성자" 를 정의해놓지 않음BUT 자동으로 호출되어 실행됨!
그리고 여기서는 이미 Human(int NTRP) 라는 생성자를 정의해놓음그럼 컴파일러가 자동으로 추가해주는 생성자는 없어야함
근데 왜 정의해놓지 않은 생성자가 호출됨?
여기서 등장하는 객체를 인자로 받는 생성자가 바로 "복사생성자"임// 복사생성자 형식 // 인자로 자기자신 객체를 받음 SoSimple(SoSimple ©);
C++ 에서 복사생성자는 일반 생성자와 다르게 취급한다따라서 복사생성자를 정의해놓지 않으면, 디폴트 복사생성자가 자동으로 삽입된다.
그리고 이 디폴트 복사생성자는 "얕은 복사"를 진행함
(얕은 복사는 무식한 복사를 말한다. 그냥 멤버값 다 무지성으로 베끼는거임
따라서 동적할당 즉, heap을 사용한 멤버가 있을때는 문제가 발생함)
그러므로 우리가 이미 생성자를 정의해놓았어도이는 복사생성자(객체를 인자로 받는)가 아니였기 때문에
컴파일러는 복사생성자가 정의되지 않은 것을 확인하고
자동으로 디폴트 복사생성자를 추가하는 거임
[2. 복사생성자]
복사생성자는 말그대로 "복사" 해주는 "생성자"임
그럼 뭘 복사해주는걸까? 당연히 객체임 ㅇㅇ
즉, 객체 간 대입연산을 했을시, 객체끼리 복사가 되어야하므로 복사생성자가 존재하는 거임
Human(const Human& copy) : NTRP(copy.NTRP) { // empty }
이게 복사생성자 코드임.
객체를 참조값으로 받고 이를 통해 멤버 간 복사 즉, " 얕은 복사 " 를 수행한다!
따라서, 동적할당한 멤버변수가 없을때는 굳이 복사생성자를 정의안해도 된다
하지만 new 를 사용하여 heap에 멤버변수를 저장했을때는 어케 해야할까
복사생성자를 직접 정의해주면 됨
class Human { private: char * name; public: // 기본 생성자 Human(char* name) { int len = strlen(name) + 1; this->name = new char[len]; strcpy(this->name, name); } // 복사생성자 Human(const Human& human) { int len = strlen(human.name) + 1; this->name = new char[len]; strcpy(this->name, human.name); } void printName() { cout << name << endl; } ~Human() { delete []name; } };
복사생성자를 직접 정의했다.
포인터변수를 멤버변수로 가지고 있어 동적할당을 직접 해줘야하기 때문 ㅇㅇ
int main() { char temp[10]; cin >> temp; Human Federer(temp); Federer.printName(); Human jhmin = Federer; jhmin.printName(); return 0; }
이렇게 하면 Federer와 내가 참조하는 동적할당된 name 배열이
같은게 아니라 다른거로, 즉 각각 가지고 있게 된다.
참고로 얕은복사가 되면 한 객체가 소멸되었을시 다른 객체에 문제가 발생한다.
아래 그림 이해 ㄱㄱ
얕은 복사 상황 멤버변수가 메모리해제 되었는데 아직도 참조하고 있는 모습! 이거 방지하려고 깊은 복사를 씀
[복사생성자를 const 와 참조형(&) 으로 선언하는 이유]
1. 참조형(&) 으로 선언하는 이유
MyClass(MyClass temp){ // code }
이런 형태인데
이럴때 복사생성자를 통해 해당 객체를 매개변수로 들어온 객체로 초기화하는게 불가능해진다
왜냐하면 매개변수와 인자 사이에 복사생성자 무한루프를 돌기 때문이다.
MyClass B; MyClass A = B; // 이게 MyClass A(B) 와 같으므로
이러면 MyClass temp = B 란 식이 호출되는데
이러면 복사생성자 무한호출된다
따라서 참조형으로 쓴다
2. const 쓰는 이유
복사생성자의 목적은 인자로 들어온 객체를 바꾸는 것이 아니다
자기자신의 멤버변수를 바꾸는게 목적임
따라서 복사생성자의 목적에 맞게끔 올바르게 사용되게 하기 위해서 const를 붙여놓는 거임
'C++' 카테고리의 다른 글
[C++] 상속(1) - 개념 (0) 2022.12.31 [C++] 복사생성자의 호출시기 (0) 2022.12.30 [C++] 생성자(constructor) (0) 2022.12.26 [C++] class (0) 2022.12.24 [C++] 참조자 (reference) (0) 2022.12.23