1. 동적 메모리 할당
#include <cstdlib>
int main() {
int* p1 = (int*)malloc(sizeof(int)*10);
free(p1);
// C언어에서의 메모리 할당
int* p2 = new int; // int 한 개, 4byte
delete p2;
// C++에서의 메모리 할당
int* p3 = new int[10];
delete[] p3;
int* p4 = new int[10][2];
delete[] p4;
}
- C언어
=> malloc으로 할당하고 free로 해지한다.
=> C언어에서는 캐스팅이 필요 없지만, C++에서 malloc사용시 반환되는 주소는 원하는 포인터 타입으로 캐스팅 해야 한다.
- C++
=> new로 할당하고 delete로 해지한다.
=> new가 반환한 주소를 캐스팅 할 필요 없다.
=> 배열의 형태로 할당한 경우는 delete[]로 해지 해야한다.
=> 배열을 delete[]가 아닌 delete로 해지하면 어떻게 될까? - C++표준문서에 delete[]를 안쓸경우에 대한 내용이 없음 -> 컴파일러마다 다른 오류를 배출할 것(미정의 동작, undefined)
- malloc은 생성자는 호출하지 못하지만 new는 생성자를 호출한다.
2. nullptr
int main() {
int* p1 = 0;
int* p2 = nullptr;
}
- C++11부터 도입된 새로운 키워드
- 널 포인터 값(null pointer value)을 나타내는 포인터 리터럴(pointer literal)
- 포인터 변수를 초기화 하기 위해 기존의 방식대로 0을 사용해도 되지만 nullptr을 사용하는 것이 안전하고 코드의 가독성을 높일 수 있다.
2.1 0의 특징
#include <iostream>
void move(int n) { std::cout << "int" << std::endl; }
void move(double d) { std::cout << "double" << std::endl; }
void move(bool b) { std::cout << "bool" << std::endl; }
void move(char* s) { std::cout << "char*" << std::endl; }
int main() {
int n = 0;
double d = 0;
bool b = 0;
char* s = 0;
move(0); // ? - int
move(0.0); // 실수 리터럴
move(false); // bool 리터럴
move((char*)0); // char*, 포인터 리터럴
move(nullptr); // char*, 포인터 리터럴
}
- 0은 정수, 실수, bool, 포인터 등의 변수를 초기화 할 때 사용 가능
위에서 0은 정수, 실수, bool, 포인터를 초기화 하고 있고, 위에 move함수는 파라미터가 다르므로 4개를 만들었다.
이 때, move(0); 으로 함수를 호출하면 무엇이 호출될까?
- 0은 정수형 literal이고 int 타입
=> 0은 int타입이지만, 실수, bool, 포인터 타입으로 암시적 형 변환될 수 있다.
그럼 int타입을 인자로 받는 move함수를 주석처리 해버린다면 move(0);은 어떤 함수를 호출할까?
- 컴파일 시간에 함수호출 타입이 모호하다고 에러가 난다.
- 리터럴(literal)
=> 소스 코드 내에서 사용되는 고정된 값
=> 변수 초기화나 구문 등에서 많이 사용된다.
=> 모든 리터럴은 데이터 타입이 있다.
literal 값 | 종류 | 데이터 타입 |
0 | 정수형 literal | int |
0.0 | 실수형 literal | double |
false | boolean literal | bool |
nullptr | pointer literal | ? |
- nullptr의 데이터 타입은 뭘까?
2.2 nullptr과 코드 가독성
int* move() { return 0; }
int main() {
auto ret = move();
// if(ret==0) { }
if(ret==nullptr) { }
}
위의 코드는 move()함수를 호출하고 반환값을 auto로 받아 ret의 타입을 결정하고 ret이 0이라면 무언가를 하겠다란 코드이다.
당신이 이 코드를 작성해놓았는데 다른사람이 이 코드를 본다고 생각해보자.
다른 사람 입장에선 move함수를 보기 전까진 0이라고 조사를 하고 있기 때문에 ret이 int, double, bool, pointer인지 모른다.
따라서 0이 아닌 nullptr일 경우 다른 사람이 보기에도 pointer타입인것을 바로 알 수 있다.
- 0을 사용하는 것보다 nullptr을 사용하는 것이 코드 가독성이 좋다.
2.3 nullptr과 데이터 타입
int main() {
0; // int
0.0; // double
nullptr; // std::nullptr_t
int* p1 = nullptr;
char* p2 = nullptr;
void(*f)() = nullptr;
int n1 = nullptr; // error
int n2 = 0;
bool b1 = nullptr; // error
bool b2(nullptr); // ok - 직접 초기화
bool b3{nullptr}; // ok - 일관된 초기화
bool b4 = {nullptr}; // error - 복사 초기화
}
- nullptr은 std::nullptr_t 타입
- std::nullptr_t 타입은 모든 타입의 포인터로 암시적 변환 된다.
- std::nullptr_t 타입은 int타입으로 변환 될 수 없다.
- std:nullptr_t 타입은 bool타입으로 직접 초기화(ditrect initialization)시 초기값으로 사용이 가능하다.
2.4 NULL과 nullptr
#include <iostream>
void move(int n) { std::cout << "int" << std::endl; }
void move(void* p) { std::cout << "void*" << std::endl; }
void idle(char* n) { std::cout << "idle" << std:: endl; }
/*
#ifdef __cplusplus
#define NULL 0
#else
#define NULL (void*)0
#endif
*/
int main() {
move(0); // int
move((void*)0); // void*
move(NULL); // int
idle(NULL); // ok
}
- C와 C++의 차이점
C | void* -> 다른 타입* / 암시적 변환 허용 |
C++ | void* -> 다른 타입* / 암시적 변환 불가 |
__cplusplus - 모든 C++컴파일러는 이 매크로가 정의되어 있다.
- NULL
=> C/C++에 따라 구현 방법이 다르다.
=> 컴파일러의 종류, 버전에 따라서도 구현 방법이 다르다.
=> 일반적으로 C++ 컴파일러는 정수 리터럴 0으로 정의 하는 경우가 많다.
!! 포인터 변수를 초기화 하거나 값을 조사 할 때는 0 또는 NULL을 사용하지 말고, nullptr을 사용하자.
'프로그래밍 > C++' 카테고리의 다른 글
[C++] 객체지향 프로그래밍의 개념(2) (0) | 2019.10.29 |
---|---|
[C++] 객체지향 프로그래밍의 개념(1) (0) | 2019.10.29 |
[C++] Explicit Casting (0) | 2019.10.25 |
[C++] 레퍼런스(reference) (0) | 2019.10.22 |
[C++] range for / if init / if constexpr (0) | 2019.10.18 |