전체코드
#include <iostream>
#include <cstring>
using namespace std;
class MyClass
{
public:
MyClass(const char _name[])
{
this->name = new char[strlen(_name) + 1];
strcpy_s(this->name, strlen(_name) + 1, _name);
cout << "기본 생성자 MyClass()" << this->name << endl;
}
MyClass(const MyClass& rhs)
{
this->name = new char[strlen(rhs.name) + 1];
strcpy_s(this->name, strlen(rhs.name) +1, rhs.name);
cout << rhs.name << "@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@" << endl;
}
//MyClass(MyClass&& rhs) : name(rhs.name)
//{
// cout << rhs.name << "########## 이동 생성자(const MyClass&& rhs)##########" << endl;
// rhs.name = nullptr;
//}
operator string() { return this->name; }
MyClass operator+(const MyClass& rhs)
{
cout << "operator+" << endl;
MyClass temp(rhs.name);
int thisNameLen = strlen(this->name);
int rhsNameLen = strlen(rhs.name);
strcat_s(temp.name, (thisNameLen + rhsNameLen + 1), this->name);
return temp;
}
MyClass& operator=(const MyClass& rhs)
{
cout << "operator=" << endl;
return *this;
}
void setName(const char _name[])
{
delete this->name;
this->name = new char[strlen(_name) + 1];
strcpy_s(this->name, strlen(_name) + 1, _name);
}
void printName()
{
cout << this->name << endl;
}
private:
char *name;
};
int main()
{
cout << "##########begin#########" << endl;
MyClass a("aa"), b("bb"), c("cc");
MyClass d = a + b + c;
a.printName();
cout << string(a) << endl;
b.printName();
cout << string(b) << endl;
c.printName();
cout << string(c) << endl;
d.printName();
cout << string(d) << endl;
cout << "##########end#########" << endl;
return 0;
}
// 출력값
##########begin#########
기본 생성자 MyClass()aa
기본 생성자 MyClass()bb
기본 생성자 MyClass()cc
operator+
기본 생성자 MyClass()bb
bbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@
operator+
기본 생성자 MyClass()cc
ccbbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@
aa
aa
bb
bb
cc
cc
ccbbaa
ccbbaa
##########end#########
MyClass라는 클래스를 만들고 인스턴스 a,b,c를 각각 정의했다.
이때는 당연히 기본생성자가 불린다.
파라미터 값으로 문자열을 받으니 _name[]으로 정의했다.
그런다음 name 포인터에 문자열을 담는 아주 간단한 기본 생성자이다.
문제는 MyClass d = a +b + c; 여기에 있다.
골때리게도 이 한줄이 이렇게 많은 생성자를 호출한다...
생성자를 호출한다는건 인스턴스가 생성된다는 뜻이고 여기에 따른 메모리, CPU Loss가 엄청나다.
여기서 주목해야할 부분은 바로 복사 생성자와 operator+ 더하기 연산자다.
복사 생성자에서는 메모리 할당 문제 때문에 Deep Copy를 수행한다. (이전 글 참조)
2020/02/01 - [C++] - C++ 복사 생성자(참조형 파라미터)
operator + 를 살펴보자.
operator+
MyClass operator+(const MyClass& rhs)
{
cout << "operator+" << endl;
MyClass temp(rhs.name);
int thisNameLen = strlen(this->name);
int rhsNameLen = strlen(rhs.name);
strcat_s(temp.name, (thisNameLen + rhsNameLen + 1), this->name);
return temp;
}
a + b === a.operator+(b)
operator+에서는 할말이 아주 많은데 천천히 하나씩 해보면
1. operator+는 a+b를 할때 불리는 연산자이다.
2. a+b는 내부적으로 a.operator+(b)랑 같다
3. 그러므로 위의 코드 operator+의 rhs 값은 오른쪽 즉 b의 인스턴스의 참조이다.
4. operator+ 내부에서 temp 인스턴스를 생성하는데 b의 name값을 가지고 생성한다.
※ 기본 생성자 MyClass()bb 출력
5. a의 name값과 b의 name값을 합쳐서(strcat_s) MyClass instance temp를 return 한다.
- (중요)이때 복사 생성자가 출력되는데 그 이유는 return temp를 할때 임시 객체를 생성하고 return 하기 때문이다.
MyClass(const MyClass& rhs)
{
this->name = new char[strlen(rhs.name) + 1];
strcpy_s(this->name, strlen(rhs.name) +1, rhs.name);
cout << rhs.name << "@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@" << endl;
}
// 출력값
bbaa@@@@@@@@@@@복사 생성자 MyClass(const MyClass &rhs)@@@@@@@@@@
6. 위의 코드는 복사 생성자가 불릴땐데 rhs.name은 bbaa이다.
그러니깐 위의 temp instance를 기반으로 복사 생성이 이루어졌단뜻!
왜냐?? a + b를 하면 + c를 또 해야되는데(d = a + b + c) a + b의 결과 값을 가지는 임시 객체를 만들어야 하기 때문에 복사 생성이 필요하단 뜻이다. 근데 도대체 이동 생성이랑 무슨 관계인데??
위의 복사 생성은 Deep Copy를 하고있다. 아니 조만간 사라질놈이 왜 Deep 카피를 하냐 성능 떨어지게 그냥 포인터로 간단하게 끝내서 성능향상을 하면 되잖아! 이 논리로 나온게 바로 이동 생성이다.
MyClass(MyClass&& rhs) : name(rhs.name)
{
cout << rhs.name << "########## 이동 생성자(const MyClass&& rhs)##########" << endl;
rhs.name = nullptr;
}
// 출력값
bbaa########## 이동 생성자(const MyClass&& rhs)##########
전체코드의 주석을 풀자.
그럼 복사 생성자 대신 이동 생성자가 불린다.
이동 생성자의 rhs는 무엇일까? 바로 temp이다. 그러니깐 return temp를 할 때 temp instance를 기반으로 이동 생성자가 불렸다는 뜻. 당연하다 복사 생성자도 temp instance를 기반으로 했으니.
이동 생성자에서는 new로 메모리 할당받아서 Deep Copy이런거 안한다.
그냥 rhs.name(이때 rhs는 temp 임시 객체 r-value 참조)의 주소 값(포인터니깐)을 name에 할당 그러니깐 주소값만 name에 넣어주고 끝이다. 왜냐하면 조만간 사라질 임시 객체니깐! 그리고 그 값을 고스란히 return 하고 아름답게 전사한다. 이때 return 한 임시 객체 값은 + c 의 연산과정에서 또 쓰이게 된다!
이로써 Deep Copy(복사생성)에서 메모리 할당받고 어쩌고 저쩌고를 포인터 하나로 아주 성능 좋게 바꿨다.
'C++' 카테고리의 다른 글
C++ 멤버함수의 메모리 위치 (0) | 2020.04.09 |
---|---|
C++ 객체 생성 (0) | 2020.04.02 |
C++ 대입 연산자 (0) | 2020.02.02 |
C++ 깊은복사 얕은복사 (0) | 2020.02.02 |
C++ 복사 생성자(참조형 파라미터) (0) | 2020.02.01 |