본문 바로가기
IT/C++(CPP)

CPP 초급 강좌 23. C++ 동적메모리 사용(new, delete, delete[]), nullptr

by 신림83 2020. 11. 2.
반응형

CPP 초급 강좌 23. C++ 동적메모리 사용(new, delete, delete[]), nullptr

동적메모리 

c++ 에서 동적 메모리를 사용하는 방법에 대해 봅시다.

 

먼저 c 에서의 메모리 할당 코드를 봅시다. 현제 c++에서도 사용할 수도 있는 코드입니다.

int* p = (int*)malloc(sizeof(int) * 10);
free(p);

malloc로 할당하고, free 로 해지합니다. 해당 코드는 int * 10의 메모리 영역을 할당합니다. 40byte

기본적으로 malloc 는 void* 형이 반환타입입니다. 원하는 형식으로 다시 캐스팅(int*) 을 하여 사용하여야 합니다.

c++ 프로젝트 에서도 아직 많이들 사용하고 있는 코드입니다. 필요할 경우도 분명 많구요.

 

그럼 이번에는 c++ 에서 지원되는 동적메모리 할당 코드를 봅시다.

int* p1 = new int;      //a
delete p1;

int* p2 = new int[10];  //b
delete[] p2;

int* p3 = new int[5, 2];//c
delete[] p3;

a 는 데이터 타입을 하나 요구하고, 지우는 코드이다.

new 형식 delete 포인터 로 코딩하여 사용한다.

 

b 는 일차원 배열 데이터를 할당하는 방법이다.

특이한 것은 배열형식을 지울 때 delete[] 를 사용한다는 것이다.

 

c 는 이차원(다중 배열) 데이터 할당을 할때 사용하는 방법니다.

자중 배열이라고 해서 delete[][], delete[,] 이런 코드 사용하지 않고, delete[]를 사용한다.

 

c++ 에서는 new, delete 키워드로 동적 메모리를 사용할 수 있다.

new로 할당된 메모리는 c의 malloc와 달리 캐스팅할 필요가 없다.

사실 new로 호출시 malloc와 가장 큰 차이점은 생성사 호출에 있다. 아직 클래스의 이해도가 높지 않을거라 가정하여 해당 내용을 자세히 설명하지 않겟지만, 이런게 있구나 정도로 봐주시면 좋을 듯합니다.

 

nullptr

포인터 변수를 초기화 할 때 어떤 코드를 사용하시나요 다음 코드를 봅시다.

int* p1 = 0;        //a
int* p2 = NULL;     //b
int* p3 = nullptr;  //c

a 는 0으로 초기화 하는 코드입니다. 많이들 사용하죠.

b 는 NULL 로 초기화 하는 코드입니다. a, b의 정확한 차이를 알고 있으신가요?

c 는 c++11 부터 추가된 새로운 키워드인 nullptr로 포인터 초기화를 한 코드입니다.

 

먼가 이유가 있으니 nullptr이 추가 된 거겠죠.

 nullptr의 도입은 안전 과 코드 가독성 을 위함입니다.

 

설명하기 전에 리터럴 이란 용어를 알면 좋습니다.

 리터럴은 데이터 그 자체를 의미합니다. 위의 코드에서는 0, NULL, nullptr 등이 리터럴이 될 수 있습니다.

 

0 이라는 녀석...

int i = 0;
double d = 0;
bool b = 0;
int* p = 0;

해당 초기화 코드들은 아무 문제없이 잘 빌드되고 동작하는 코드입니다. 그닥 뭐 특이점이 없습니다. 그럼 아래 코드를 추가로 봅시다.

 

void f(int value)    { cout << "f(int value)" << endl; }
void f(double value) { cout << "f(double value)" << endl; }
void f(bool value) { cout << "f(bool value)" << endl; }
void f(int* value) { cout << "f(int* value)" << endl; }

int main()
{
	int i = 0;
	double d = 0;
	bool b = 0;
	int* p = 0;

	f(0); //뭐가 호출될까요?
}

f(0)는 뭐가 호출될까요? 모두 0 값으로 초기화가 가능한데?

 

출력결과

정답은 f(int) 입니다. 왜 일까요?

그것은 0 은 리터럴인데, 리터럴은 종류와 데이터 타입이 있습니다.

0과 같은 경우는 정수형 literal 에 int형 데이터 타입입니다. 그래서 f(int) 형으로 호출되는 것입니다.

 

 

그럼 다음 경우는 어떨까요?

//void f(int value)    { cout << "f(int value)" << endl; }
void f(double value) { cout << "f(double value)" << endl; }
void f(bool value) { cout << "f(bool value)" << endl; }
void f(int* value) { cout << "f(int* value)" << endl; }

int main()
{
	int i = 0;
	double d = 0;
	bool b = 0;
	int* p = 0;

	f(0); //뭐가 호출될까요?
}

매칭되던 타입을 주석처리 하여 삭제하였습니다. 어떻게 될까요?

이제부터는 f(0) 에서 영은 정확히 매칭되는 타입이 존재하지 않습니다.

f의 오버로드된 함수 모두가 가능성이 있어져서 호출이 모호하게 됩니다. 이런 경우는 빌드부터 되지 않습니다.

 

컴파일러는 뭐를 호출해야 되는지 모른다.

각 함수를 정확히 호출하기 위해 해당 리터럴로 호출하는 코드를 작성해봅시다.

	f(0);      //a
	f(0.0);    //b
	f(false);  //c
	f((int*)0);//d

호출된 순서

a 0

 정수형 literal, int 데이터 타입

b 0.0

 실수형 literal, double 데이터 타입

c false

 boolean literal, bool 데이터 타입

d (int*)0

 nullptrl 등장이전 포인터 리터럴 타입이 없었다. 위와 같은 방식으로 필요할 경우 호출하곤 했다.

 

존재하지 않았던 포인터 리터럴 타입의 등장

 

nullptr

	f((int*)0);//d
	f(nullptr);//d2

d, d2의 결과는 같다.

nullptr 은 pointer literal 이며, std::nullptr_t 데이터 타입이다.

 

가독성 문제?

아래 코드를 봅시다.

int* p = nullptr;

//...
//...
//...

if (p == 0)
{
	//작업
}

 해당 코드들이 한눈에 보일때는 p라는 것이 포인터라는 것을 알 수 있겠지만 많약 함수내부 코드이고 선언부와 조건문이 거리가 멀다(200라인 이상 차이가 있다.) 라 가정하자. 당신이 확인할 수 있는 코드는 if(p==0) 뿐이고 이후 내부에서 p를 뭔가 써야 한다면 p가 실제 어떤 변수인지, 아니면 포인터인지 알수 있는 방법이 있는가? 아마도 선언부를 보고 다시 와야 하겠지.

 

이럴땐 명시적으로 포인터라면 nullptr을 사용하면 어떨까?

if(p == nullptr) 

한눈에 포인터로 사용하고 없는지 확인하는 코드일줄 알 수 있을 것이다.

 

다음 코드는 어떨까?

auto p = f();
if (p == 0)
{
	//작업
}

 p는 뭘까? 포인터라면 비교에 nullptr 표시해 주면 더 좋지 않을까?

 

std::nullptr_t

 nullptr 이라는 리터럴은 std::nullptr_t 라는 데이터 형태를 가진다.

 

std::nulptr_t 는

 모든 타입의 포인터로 암시적 변환이 된다.

 정수 타입으로 변환 되지 않는다.

 bool 값으로 직접 초기화는 가능하다. 

 

코드로 정리하면 이러 하다.

	int* p1 = nullptr;  //ok 포인터 암시적 변환
	void* p2 = nullptr; //ok 포인터 암시적 변환
	void(*f) = nullptr; //ok 함수 포인터 암시적 변환

	int n = nullptr;    //error 정수로 변환 안됨

	bool b1 = nullptr;  //bool 형에 바로 대입 안됨
	bool b2{ nullptr }; //ok 직접 초기화로는 사용 가능

 

공부합시다. 배워두면 다 쓸모가 있어요.

봐주셔서 감사합니다.

 

반응형

댓글