본문 바로가기

Today I Learned

23.02.14 - 클래스 내 static, 복사 생성자

클래스 내 static

class CObj
{
public:
	CObj() //: m_iA(100) (x)
	{
		//m_iA = 100; (x)
	}

public:
	static int			m_iA; // 클래스 변수(정적 멤버 변수)

클래스 내 static 변수는 어떻게 초기화할까?

생성자로도 이니셜라이저로도 초기화가 불가능하다.

int		CObj::m_iA = 0;

초기화하려면 클래스 위 코드처럼 네임스페이스를 이용해서 초기화 해야 한다.

이렇게 초기화하기 어려운데 왜 쓰는 걸까? 

정적 멤버는 아무리 객체가 많아도 클래스에 하나만 생성되고 모든 객체에서 접근이 가능하다.

또한 전역변수인데 은닉성을 통해 숨기고 싶을 때 클래스 내부에 static으로 선언하기도 한다.

 

	// 클래스 함수(정적 함수)
	static void	Print(void)
     {
		m_iB = 400; // (x) 멤버 변수

		m_iA = 400; // (o) 정적 변수
     }
     
     ...
     
     int main()
     {
     	CObj::Print();
     }

클래스 내 static함수 또한 존재한다. 멤버 변수에 접근할 수 없고 정적 변수만 사용할 수 있다.

객체 생성 없이 네임스페이스를 이용해 함수를 호출할 수 있다는 특징이 있다.

 

 

 

복사 생성자

복사 생성자는 객체를 복사 생성할 때 호출되는 생성자로 기본 생성자처럼 디폴트 복사 생성자가 존재한다.

디폴트 생성자는 얕은 복사를 수행한다.

	CObj(const CObj& rhs) : m_iA(rhs.m_iA)
	{
    
	}

기본형은 매개변수를 const 클래스&로 받고 현재 객체의 멤버 변수의 복사 대상의 멤버 변수 값을 대입한다.

 

복사 생성자가 사용되는 상황은 세가지가 있다.

void main(void)
{
	CObj	Temp(100);
	CObj	Dest(Temp);	
}

1.원본 객체를 매개변수로 넣어 객체를 생성할 때

void Print(CObj _Src)
{
	_Src.Render();
}

2.함수의 매개변수를 객체로 받을 때 (매개변수는 원래 대입받은 대상을 복사하니까)

CObj Get_Obj()		// 3. 함수의 반환 타입이 객체 타입인 경우
{
	CObj Test(2000);

	return Test;
}

3.함수가 객체 타입으로 반환할 때 

함수가 종료될 때 지역적으로 선언한 Test가 삭제되고 Test 객체를 복사한 임시 객체가 반환된다. 

반환된 임시객체는 다음 행으로 넘어가면 바로 소멸된다.

 

class CObj
{
private:
	int* m_pA;

public:
	CObj()
	{
		m_pA = new int;
	}

	CObj(const CObj& rhs)
	{
		//m_pA = rhs.m_pA;		// 얕은 복사(shallow copy)에 의한 메모리 해제 문제 발생

		m_pA = new int;			// 깊은 복사(deep copy)에 의한 메모리 문제 해결
		*m_pA = *rhs.m_pA;              //새 메모리 할당 후 값 복사 
	}

	~CObj()
	{
		delete m_pA;
	}
};

복사 생성에도 얕은 복사, 깊은 복사가 있다. 생성자에서 동적할당이 일어날 때 발생하는데,

복사 생성자에서 동적할당한 메모리의 포인터를 주소값만 복사해보자. 이걸 얕은 복사라고 한다.

그럼 rhs를 복사해서 생성한 객체의 m_pA도 rhs.m_pA가 가리키는 메모리를 가리킬 것이다.

그러면 소멸자에서 메모리를 해제할 때 두 객체가 같은 메모리를 가리키고 있으므로 중복해서 메모리를 해제하는 문제가 발생할 것이다. 그래서 복사 생성하는 객체의 멤버가 새로운 메모리를 할당하도록 하는 것이 깊은 복사이다.