생성자 종류

  • 기본 생성자 - 매개변수 존재 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 &param 
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 &param)
{
    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);

'💻 Computer Science > C & C++' 카테고리의 다른 글

[C++] 상속  (0) 2024.06.02
[C++] 연산자 다중 정의  (0) 2024.04.15
[C++] 네임스페이스  (0) 2024.02.05
[C++] 함수  (1) 2024.02.05
[C] Makefile & CMake  (0) 2024.01.23