본문 바로가기
프로그래밍/C++

객체 포인터

by 리뷰하는 (게임)프로그래머_리프TV 2010. 3. 29.




객체 포인터,

간단하게 보면 class를 가르키는 포인터로 볼 수 있겠지만,

이거, 생각보다 재밋으면서도 어렵다.

일단 따로 보자.

	int n;
	int* pn = &n;​


int n을 만들고, 포인터 pn이 가르킨다.
(n의 값이 쓰레기인건 신경쓰지 말자)
그렇다면 여기서, 의문을 가져 보자.

	char* c = (char*)new int;
	int* n = (int*)new char;

	delete c;
	delete n;​


어떻게 될까?
컴파일에서 문제도 일어 나지 않고, 실행도 된다.
여기서 파악해야 할 것은, 메모리의 크기이다.
char* c 은 1바이트
int* n은 4바이트를 가르키게 되는데,
저런식으로 하게 되면 c는 new int의 1바이트 부분만 가르키게 되고,
n은 새로 생성된 1바이트를 넘쳐서 가르키게 된다.

어려운가, 어쩔 수 없다.
객체 포인트를 살펴 보자.

#include <iostream>

class AAA
{
public:
	AAA(){}  // 아무것도 없으면 허전하니 생성자라도;
};

class BBB : public AAA
{
public:
	BBB(){}
};

class CCC : public BBB
{
public:
	CCC(){}
};

void main()
{
	AAA* Cp_a;
	BBB* Cp_b;
	CCC* Cp_c;
}​



객체 포인트라고 해도 사실 우리가 알고 있던 포인터와 크게 다르지 않다.
오히려 그냥 단순히 AAA클래스를 가르키는 Cp_a 정도로 이해해도 상관없다.
그렇다면 여기서 생각해 봐야할 소스 코드가 있다.

void main()
{
	AAA* Cp_a = new AAA;
	AAA* Cp_b = (AAA*)new BBB;
	AAA* Cp_c = (AAA*)new CCC;
}​


뭔가 이상하다고 느낄지도 모르겠다.
분명 객체 포인트는 AAA* 인데 어째서 new BBB가 들어 가는가(아무리 형변환을 했다고 하더라도.)
의문점을 해결하기 전에 한가지더 알아 보자.

#include <iostream>

class AAA
{
	int n;
public:
	AAA(){}	// 아무것도 없으면 허전하니 생성자라도;
};

class BBB : public AAA
{
	char n2;
public:
	BBB(){}
};

class CCC : public BBB
{
	double n3;
public:
	CCC(){}
};

void main()
{
	std::cout << sizeof(AAA) << std::endl;
	std::cout << sizeof(BBB) << std::endl;
	std::cout << sizeof(CCC) << std::endl;
}​


결과값이 어떻게 나올것 같은가????
결과는 다음과 같다.


어째서 저렇게 나오는 것인가.
변수의 크기에 대해서 자세히 파악을 하고 있다면,
int 4바이트
char 4바이트
double 8바이트
인 것이다.
4, 8, 16, 이제 좀 이해가 가는가?
(왜 char가 1바이트가 아니라 4바이트 인지는 메모리 구조를 조금 자세히 파악하면 알 수 있다. 나도 확실히는 잘 모른다.)
즉, CCC클래스는 AAA 클래스 + BBB 클래스인 것이다.
자 그렇다면 원래 처음문제 점 이였던,
AAA* = (AAA*) new CCC;
의 경우에 대해서 다시 한번 파악해 보자.
소스를 보자.

#include <iostream>

class AAA
{
	int n;
public:
	AAA(){}	// 아무것도 없으면 허전하니 생성자라도;
	void Test()
	{
		std::cout << "AAA클래스의 Test" << std::endl;
	}
};

class BBB : public AAA
{
	char n2;
public:
	BBB(){}
	void Test()
	{
		std::cout << "BBB클래스의 Test" << std::endl;
	}
};

class CCC : public BBB
{
	double n3;
public:
	CCC(){}
	void Test()
	{
		std::cout << "CCC클래스의 Test" << std::endl;
	}
};

void main()
{
	AAA* Cp_a = new AAA;
	AAA* Cp_b = (AAA*)new BBB;
	AAA* Cp_c = (AAA*)new CCC;

	Cp_a->Test();
	Cp_b->Test();
	Cp_c->Test();
}​


결과는 다음과 같다.


여기서 한번 의문을 가져 볼 수 있다.
AAA* 라고는 하지만 생성된 객체는 BBB, CCC 일텐데,
어째서 AAA의 Test함수만 호출 하는가.
이것은 좀전에 설명했던, char* c = (char*)new int;에서 했던 말과 동일하다.
AAA의 객체 포인터가 가질 수 있는 메모리의 크기는 딱 AAA 클래스의 크기 만큼이다.
비록 BBB,CCC가 생성되서 힙메모리 어딘가에서 생성이 되었다고 하더라도,
AAA 객체 포인터가 가르 킬수 있는 크기는 딱 AAA 클래스 크기 만큼인 것이다.
음, 이해가 가는가.
내가 설명했지만 도저희 이해가 아니될것 같다.
하지만 더이상의 설명을 뭐라고 해야 할지를 모르겠다.

여기서 가질 수 있는 의문이 바로 이것이다.
"그렇다면 왜 저렇게 해주어야 하는가."
"그냥 CCC* = new CCC; 로 하면 안되는 것인가.?"
실제로 그렇게 해도 되지만
그것에 대한 예제를 살펴 보자.

#include <iostream>

class AAA
{
	int n;
public:
	AAA(){}	// 아무것도 없으면 허전하니 생성자라도;
	void Test()
	{
		std::cout << "AAA클래스의 Test" << std::endl;
	}
};

class BBB : public AAA
{
	char n2;
public:
	BBB(){}
	void Test()
	{
		std::cout << "BBB클래스의 Test" << std::endl;
	}
};

class CCC : public BBB
{
	double n3;
public:
	CCC(){}
	void Test()
	{
		std::cout << "CCC클래스의 Test" << std::endl;
	}
};

class DDD
{
public:
	int cnt;
	AAA* arr[3];
	DDD(){ cnt = 0; }
	void SetArr( AAA* _aaa )
	{
		arr[cnt++] = _aaa;
	}
};

void main()
{
	AAA* Cp_a = new AAA;
	AAA* Cp_b = (AAA*)new BBB;
	AAA* Cp_c = (AAA*)new CCC;
	DDD d;

	std::cout << Cp_a << std::endl;
	std::cout << Cp_b << std::endl;
	std::cout << Cp_c << std::endl;

	d.SetArr(Cp_a);
	d.SetArr(Cp_b);
	d.SetArr(Cp_c);

	std::cout << std::endl;

	std::cout << d.arr[0] << std::endl;
	std::cout << d.arr[1] << std::endl;
	std::cout << d.arr[2] << std::endl;
}​


결과는 다음과 같다.

이제 감이 오는가.
비록 사용할 수 없을 지언정,
DDD라는 클래스에서 AAA, BBB, CCC(물론 다 상속 관계)
모두 다 받을 수 있다는 것이다.
즉 정리하면,
최상위 클래스의 객체 포인터는,
하위 클래스들의 포인터를 받을 수 있다는 것이다.

다른건 다 기억 하지 못하여도 좋다.
단지 AAA, BBB, CCC 클래스의 주소를,
모두 AAA의 객체 포인트에서 받을 수 있다는 것만 인식 하자.
여기서 주의할 점은,
그 반대는 절.대. 해서는 안된다는 것과.
(그 이유는 char, int를 생각해라 int*가 가르키는 4바이트 중 char의 1바이트를 빼면 과연 나머지 3바이트는 어디를 가르킬것인지는 아무도 모른다.)
그렇다면 AAA의 객체 포인터에 넣은 것은 좋은데, BBB, CCC의 Test() 함수를 실행 할려면 어떻게 해야 하겠는가에 대해서는.
추후에 다시 포스팅을 하도록 하겠다.

ps : 세상에 쉬운건 하나도 없구나 -_ -;

'프로그래밍 > C++' 카테고리의 다른 글

함수 오버라이딩(overriding)  (0) 2010.04.03
객체 레퍼런스  (0) 2010.04.03
IS-A, HAS-A 관계  (1) 2010.03.27
상속의 형태  (0) 2010.03.26
상속의 객체 생성, 소멸 과정  (0) 2010.03.26