생성자 종류
- 기본 생성자 - 매개변수 존재 x
- 다중 정의 생성자 - 매개변수 존재(변환 생성자 - 매개변수가 1개)
- 복사 생성자(r-value 참조)
- 이동 생성자
복사 생성자
- Deep copy : 대상체도 복사
- Shallow copy : 복사하여 같은 대상체를 참조(기본 복사 생성자)
class CTest
{
public:
CTest()
{
m_nData = new int(5);
}
CTest(const CTest &rhs)
{
this->m_nData = rhs.m_nData;
}
~CTest()
{
delete m_nData;
}
int m_nData = 0;
};
- 포인터 멤버 변수에 대해서는 shallow copy를 했을 때 같은 포인터를 가리키기 때문에 소멸자에서 에러 발생
- 복사 생성자를 정의해서 deep copy 해주어야 함
- 클래스를 인자로 받을 때는 반드시 참조자를 사용, 참조자를 사용할 때는 반드시 const 추가 -> 성능 향상의 지름길
class CTest
{
public:
CTest()
{
m_nData = new int(5);
}
CTest(const CTest &rhs)
{
this->m_pData = new int(*rhs.m_pData);
}
~CTest()
{
delete m_nData;
}
int* m_pData = nullptr;
};
- CTest 클래스의 인스턴스 a, b가 있을 때, b = a;에서도 같은 포인터를 소멸하는 에러 발생
- deep copy의 복사 생성자를 정의할 때는 대입 연산자도 추가로 정의해주어야 함
CTest& operator =(const CTest &rhs)
{
*m_pData = *rhs.m_pData;
return *this;
}
- 함수 호출에서 클래스를 인자로 넘길 때 보통 복사 생성자가 호출
- 불필요한 메모리 사용을 줄이기 위해 참조자를 사용 -> CMyData ¶m
class CMyData
{
private:
int m_nMyData;
public:
CMyData(int param)
{
m_nMyData = param;
}
};
void TestFunc1(CMyData param) // 복사 생성자 호출
{
}
CMyData TestFunc2()
{
}
int main(int argc, char* argv[])
{
CMyData a(10);
TestFunc1(a);
CMyData b = TestFunc2(); // 복사 생성자 호출
return 0;
}
변환 생성자
class CTest
{
public:
CTest()
{
m_nData = new int(5);
}
CTest(int nParam)
{
m_pData = new int(nParam);
}
CTest(const CTest &rhs)
{
this->m_pData = new int(*rhs.m_pData);
}
~CTest()
{
delete m_nData;
}
int GetData() const
{
return *m_pData;
}
void SetData(int nParam)
{
*m_pData = nParam;
}
int* m_pData = nullptr;
};
void TestFunc(const CTest ¶m)
{
cout << param.GetData() << endl;
}
int main()
{
TestFunc(5);
return 0;
}
- TestFunc는 인자로 CTest를 받지만, 5를 입력해도 오류가 나지 않음 -> 컴파일러가 변환 생성자가 있는지 확인하고 호출해 TestFunc(CTest(5)); 로 처리해버림 -> 사용자는 모르는 임시 객체가 생성됨 -> 좋지 못함
- 이를 방지하기 위해 변환 생성자에 explicit를 추가해야 함 ex) explicit CMyData(int nParam)
형식 변환 연산자
class CTest
{
public:
operator int(void)
{
return *m_pData;
}
int* m_pData;
};
int main() {
CTest a;
// 둘이 같다
std::cout << *a.m_pData << std::endl;
std::cout << (int)a << std::endl;
}
임시 객체
int a = 3 + 4;
- 여기서 int의 instance는 a, 3, 4에서 추가로 3과 4를 더한 7(임시 객체)까지 총 4개
- 만약 자료형의 크기가 크다면, 임시 객체 때문에 문제가 발생할 수 있음
이동 생성자
- 자료형의 크기가 크다면 복사 생성자(deep copy)를 호출할 때, 임시 객체 생성에 의해서 순간적으로 많은 양의 메모리가 필요해짐 -> 성능 저하
- 이를 해결하기 위해 나온 생성자가 이동 생성자
- 곧 사라질 임시 객체에 대해서 참조자를 이용해 추가적인 메모리 사용을 막음
- 즉, r-value가 임시 객체일 때 사용
CMyData(const CMyData&& rhs);