저희는 기본적으로 변수를 선언하고 데이터 값을 입력하는 과정을 다음과 같이 진행합니다.
int num_1 = 10;
int num_2 = num_1;
이런식으로 선언하는 것이 일반적입니다.
하지만 C++에서는 컴파일러가 다음과 같이 바꿔서 계산을 하게 됩니다.
int num_1(10); // int num_1 = 10;
int num_2(num_1); // int num_2 = num_1;
이렇게 보면 함수 안에 매개변수를 넣는 것 처럼 보입니다.
실제로도 비슷합니다.
즉, C++은 C와 다르게 int num_1 = 10; 의 과정을 int num_1(10)으로 해석한다는 말입니다.
그렇다면 왜? 굳이? 이런식으로 바꿔 진행하느냐.....
바로 Class 때문입니다.
Class를 선언한 후 사용하게 되면 Class엔 여러가지가 포함되게 됩니다.
그리고 Class안에 함수를 선언, 동적 메모리 할당 등..... 여러가지를 할 수 있게 되죠.
이러한 것들로 인해 대입연산 보단 함수 호출 형태로 받아 C++에서 정의한 복사 생성자를 통해
이를 사용자가 직접 전달 값을 변경해 줄 수 있도록 했습니다.
말로는 설명도 어렵고 이해도 안가니 코드로 설명하겠습니다.
(※만약 visual studio 2017 이상 버전을 사용하고 계신다면 이 글을(클릭) 읽어주세요. 그래야 컴파일 에러가 안납니다.)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class A {
int num;
char * name;
public:
A(int num, char * name) {
this->num = 10;
setName(name);
}
void setName(char * name) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
void _printName() {
printf("%X \n", name);
}
};
int main(void) {
A a_1(10, "Hwan");
A a_2 = a_1;
a_1._printName();
a_2._printName();
return 0;
}
a_1 과 a_2의 char *name이 가르키는 주소 값이 같은걸 알 수 있습니다.
33행을 보면 A a_2 = a_1을 해주고 있습니다. 즉 대입해주고 있습니다.
이말은 a_1 이 갖고 있는 값들은 a_2에게 전달하고 있는 겁니다.
즉!! 일반적인 생성자 호출이 된 것이 아니라, 값만 대입해 주고 있다는 소리입니다.
따라서 생성자는 호출이 안되게 되고 결국 아래 그림과 같은 모습을 하게 됩니다.
결국 a_1의 name의 값을 바꾸게 된다면 a_2의 name의 값도 바뀌게 됩니다. 어떻게 보면 참조자 형태가 되버린 것입니다.
이는 복사 생성자가 있기 때문인데, 사용자가 선언을 안해도 컴파일 시 자동으로 디폴트 복사 생성자가 실행이 됩니다.
디폴트 복사 생성자는 다음과 같은 형태를 갖고 있습니다.
A(const A &ref) <<< 이 코드가 자동으로 생성되어 실행되게 됩니다.(사용자가 굳이 정의를 하지 않으면)
요 복사 생성자가 있기 때문에 int num(10); 이라는 문장도 성립이 가능하게 되는 것입니다.
그렇다면, 저희가 A a_2 = a_1;을 실행 시켰을 때 기대의 모습은 아래와 같습니다.
이러한 과정이 되기 위해서는 복사 생성자를 수정해 줄 필요가 있습니다.
#include <iostream>
#include <cstring>
using namespace std;
class A {
int num;
char * name;
public:
A(int num, char * name) {
this->num = 10;
setName(name);
}
A(const A &ref) { //디폴트 복사 생성자
num = ref.num;
name = ref.name;
}
void setName(char * name) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
void _printName() {
printf("%X \n", name);
}
};
짜잔!! 주소값이 달라진 것을 확인할 수 있었습니다.
이렇게 사용자가 직접 복사 생성자를 정의해 주는 것이 좋습니다.
물론, Class를 추가할 때 마다 new 키워드를 사용해 여러번 동적할당을 할 수도 있지만, 분명한 것은
클래스의 값을 복사해야할 상황도 온다는 것입니다. 때문에 이 개념을 잘 알아둬야 합니다.
다음은 왜? 복사생성자가 저렇게 생겼는지 설명하겠습니다.
댓글