[C++] 객체 포인터

WestCoast·2022년 3월 3일
1

C, C++

목록 보기
6/12

객체와 객체 포인터의 차이


class Pet
{
public:
	int _hp = 100;
    int _age = 3;
};

int main()
{
	// Stack에 올라간다.
    // sizeof(pet) = 8byte
	Pet pet;
    
    // pet 자체는 Stack에 올라가는 것이 맞다.
    // pet이 담고 있는 것은? '주소'값이다.
    // 여기서 '주소'는 pet의 실제 객체를 '가리키고' 있다.
    // pet의 실제 객체는 Heap 메모리에 올라가 있다.
    // sizeof(pet) = 4byte = 주소값의 크기
    // sizeof(*pet) = 8byte = 실제 pet 객체의 크기
    Pet* pet = new Pet();
	
    return 0;
}

포인터로 선언해야 하는 이유?


  1. 상속 관계에 있는 클래스들을 바꿔가며 사용할 경우
  2. 생명주기의 관리를 각각 해야할 경우
  3. 클래스 안에 클래스를 가질 경우, 크기가 비대해지는 것을 방지

1. 상속 관계에 있는 클래스들을 바꿔가며 사용할 경우

#include<iostream>
using namespace std;

class Pet
{
public:
	int _hp = 100;
    int _age = 3;
};

class RabbitPet : public Pet
{
public:
	RabbitPet(int color) : _color(color)
	{

	}

public:
	int _color;
};

class TurtlePet : public Pet
{
public:
	TurtlePet(int sex) : _sex(sex)
	{

	}

public:
	int _sex;
};

int main()
{
	// -------- 기본적으로 클래스를 만들었을 때 --------
    
	RabbitPet rabbitPet(1111);
	TurtlePet turtlePet(00);

	Pet pet;
    // pet에 rabbitPet을 넣어줌으로써 rabbitPet에 추가된 멤버들은 잘린다.
    // Pet 		 [ _hp, _age]
    // RabbitPet [ _hp, _age, _color ]
    // 즉, _color 가 잘려나간다.
	pet = (Pet)rabbitPet;

	// ------------ 클래스 포인터의 경우 ------------

	RabbitPet* rabbitPetPtr = new RabbitPet(2222);
	TutlePet* turtlePetPtr = new TurtlePet(11);

	Pet* petsPtr[2];
    // petsPtr[0]에 rabbitPetPtr을 넣어준다.
    // 잘려나가는 것이 있을까? -> 없다.
    // 왜? '주소'값을 넣어주는 것이기 때문!
    // 주소값은 4byte 고정이다. 커지거나 작아질 일이 없다.
	petsPtr[0] = rabbitPetPtr;
	petsPtr[1] = turtlePetPtr;

	// 이와 같이 Pet*을 RabbitPet*으로 캐스팅하여 
    // rabbitPetPtr의 멤버에 모두 접근가능하다.
	cout << ((RabbitPet*)petsPtr[0])->_color << endl;

	return 0;
}

2. 생명주기의 관리를 각각 해야할 경우

3. 클래스 안에 클래스를 가질 경우, 크기가 비대해지는 것을 방지


#include<iostream>
using namespace std;

class Pet
{
public:
	int _hp = 100;
	int dummy[500];
};

class RabbitPet : public Pet
{
public:
	RabbitPet(int color) : _color(color)
	{

	}

public:
	int _color;
};

class TurtlePet : public Pet
{
public:
	TurtlePet(int sex) : _sex(sex)
	{

	}

public:
	int _sex;
};

class Player
{
public:
	// 생성자에서 Pet* 를 받아주도록 만들어준다.
	Player(Pet* pet) : _pet(pet)
	{
	}

	~Player()
	{
    	// _pet의 생명주기가 Player 클래스와 함께하지 않으므로,
        // ~Player() 에서 _pet을 delete해준다.
		delete _pet;
	}

public:
	// Pet* 로 선언한다.
	// - Pet 클래스에 dummy[500] 이라는 큰 데이터가 있지만, 
	//    Player는 Pet의 주소만 들고 있으므로 4byte라는 작은 크기를 가진다.
	// - Pet 으로 선언할 경우 RabbitPet 등을 넣어주면 정보가 손실된다.(Slicing Problem)
	Pet* _pet;
};

int main()
{
	// 이와 같이 Player 생성 시에 RabbitPet* , TurtlePet* 을 선택하여 만들어줄 수 있다.
	// 이렇게 객체 포인터를 이용해주면 상속관계에 있는 클래스들도 정보가 손실되지 않는다.
	Player* player1 = new Player(new RabbitPet(1111));
	Player* player2 = new Player(new TurtlePet(1));

	delete player1;
    delete player2;

	return 0;
}
profile
게임... 만들지 않겠는가..

0개의 댓글