힙메모리상의 속성 member data on the heap

클래스의 멤버 중에는 포인터도 포함될 수 있다.

 

포인터는 기본 자료형을 가리킬 수도 있고 또 다른 객체를 가리킬 수도 있다. 여기가 이제 헷갈리는 부분이다.

 

지난 포스팅에서 C++에서는 스택과 힙메모리에 객체를 생성할 수 있었다.

 

그러면 클래스안의 포인터는 어떻게 사용할까? 포인터가 가리키는 주소는 스택인가 힙인가?

 

결론을 말하면 포인터는 힙영역을 가리킨다.

 

메모리 영역으로 구분하면 아래와 같이 볼 수 있다.

 

*스택 객체 : 스택에 생성

   ∟ 포인터변수 : 스택에 생성

        ∟ 가리키는 값 : 힙에 new 생성

 

*힙 객체 : 힙에 new 생성

   ∟ 포인터변수 : 힙에 생성

        ∟ 가리키는 값 : 힙에 new 생성

 

여기서 중요한 것은 new 로 생성하는 값이다. C++에서 new 로 생성한 값은 반드시 delete 로 삭제해야 한다. 힙 객체와 별도로 객체의 속성에 할당한 메모리를 해제하지 않으면 그대로 남아있다. 이것은 눈에 잘 보이지 않아서 컨트롤하기가 어렵다.

 

예제를 통해서 알아보자.

 

*예제인 MyDog 클래스에서 int * age 는 정수형 포인터 속성이다. getter setter 를 보면 포인터를 사용하고 있다.

 

 

생성자와 소멸자에 new와 delete 가 쌍으로 들어가 있다. 스택에 객체를 생성해도 new 키워드를 사용하면 힙을 사용하는 것에 주의한다. 스택에서는 포인터로 주소만 가지고 있는 형태이다.

 

 

스택에 1개, 힙에 1개씩 두번 생성하고 상태값을 확인한다.

 

 

둘리 객체가 사용하는 age를 소멸후에 다시 새롭게 용이 객체를 생성했더니 같은 주소를 사용한다. 이것은 둘리에서 사용하던 age 의 메모리 영역이 해제되었음을 뜻한다. (실행시 힙에 할당하는 주소값이 달라질 수 있다) 만일 객체는 소멸자 ~로 소멸시켰는데 객체 안에서 new 로 할당한 메모리를 해제하지 않는다면 다시 그 메모리를 해제시킬 방법이 없다.

 

이는 결국 메모리 유출(Memory Leak)이 되어 시스템의 자원을 소모할 것 이다.

 

 

main 함수에서 생성자를 호출할 때 클래스의 멤버변수(속성)가 포인터 형인지 신경쓰지 않는다. 마찬가지로 사용자도 외부 라이브러리 클래스를 사용할 때 내부의 자료형 (private)에 대해서 알 필요가 없다. 매개변수만 알 수 있다면 사용할 수 있다. 이는 객체지향프로그래밍의 특징인 정보의 은닉화를 의미한다. 좋은 라이브러리는 메모리 관리능력이 좋을 것이며 나쁜 라이브러리는 그렇지 않을 것 이다.

 

 

*예제코드

#include <iostream>
#include <string>

using namespace std;

#define Line cout <<"\n-----------------------------\n"

class MyDog
{
public:
	MyDog(string name);
	~MyDog();
	int getAge() const { return *age; }
	void setAge(int age_new) { *age = age_new; }
private:
	string name = "기본";
	int * age;
};


MyDog::MyDog(string name)
{
	this->name = name;
	cout << " 생성자 호출 : " << this->name;
	age = new int(2);
	cout << ", age의 주소 : " << age << endl;
}
MyDog::~MyDog()
{
	cout << " 소멸자 호출 : " << this->name;
	delete age;
	age = NULL;
	cout << ", age의 주소 : " << age << endl;
}

int main()
{
	MyDog WangWang = MyDog("왕왕이 스택");
	MyDog* ptrDoolly = new MyDog("  둘리   힙");

	ptrDoolly->setAge(5);

	Line;
	cout << " 스택 객체 age : " << WangWang.getAge() << endl;
	cout << " 힙   객체 age : " << ptrDoolly->getAge() << endl;
	Line;
	delete ptrDoolly;

	Line;
	MyDog* ptrYong = new MyDog("  용이   힙");
	delete ptrYong;
	Line;
	
	return 0;
}

공유하기

facebook twitter kakaoTalk kakaostory naver band