new / delete
C언어에서 메모리 할당할 때 malloc을 사용한다. 해제할 때는 free를 사용한다. C언어에서는 malloc사용시 캐스팅이 필요없다.
C++에서 malloc 사용 가능하다. 그러나 반환되는 주소는 원하는 포인터 타입으로 캐스팅 해야 한다.
#include <cstdlib>
int main()
{
int* p1 = (int*)malloc(sizeof(int)*10);
// malloc 사용시 c에서는 (int*) 캐스팅 없이 사용가능하지만
// c++에서는 (int*)와 같은 캐스팅이 필요하다.
free(p1);
}
C언어에서의 동적 메모리 할당
- new로 할당하고 delete로 해지한다.
- new가 반환한 주소를 캐스팅 할 필요가 없다.
- 배열 형태로 할당한 경우 delete[]로 해지 해야 한다.
malloc은 메모리 공간만을 만들어 준다면 (생성자 미호출), new는 메모리 공간을 만들면서 생성자를 호출한다.
#include <cstdlib>
int main()
{
int* p2 = new int; // int 1 개, 4byte 할당
delete p2;
int* p3 = new int[10];
delete[] p3; // 배열 지울 때는 [ ] 넣어줘야 함.
int* p4 = new int[10][2]; // int 20개 * 4 byte = 80 byte
delete[] p4; // 2차원 배열 지울 때도 [ ] 하나만 넣어주면 된다.
int* p5 = new int[10];
delete p5; // 불가! undefined : 컴파일러마다 동작 방식이 다름.
}
nullptr
- c++11 부터 지원하는 새로운 키워드
- 0을 사용해도 되지만, nullptr은 가독성을 높이고 더 안전하다.
int main()
{
int* p1 = 0;
int* p2 = nullptr;
}
nullptr : pointer literal
리터럴?
- 리터럴은 소스 코드 내에서 사용되는 고정된 값이다.
- 변수 초기화, 구문 등에서 많이 사용된다.
- 모든 리터널을 데이터 타입이 있다.
0은 정수, 실수, bool, 포인터 등의 변수 초기화 할때 사용 가능하다.
int main()
{
int n = 0;
double d = 0;
bool b = 0;
char* s = 0;
}
해당 예제에서 foo(0); 를 호출하면 0 값을 int로 판단하여 foo (int n)이 호출된다!
#include <iostream>
void foo(int n) { std::cout << "int" << std::endl; }
void foo(double d) { std::cout << "double" << std::endl; }
void foo(bool b) { std::cout << "bool" << std::endl; }
void foo(char* s) { std::cout << "char*" << std::endl; }
int main()
{
int n = 0;
foo(0); // int 로 인식된다.
}
아래와 같이 foo(int n) 함수가 없을 때, foo(0); 를 호출하면 double, bool, char* 중 어느 것을 호출해야 할지 모호해서 에러가 발생한다.
#include <iostream>
//void foo(int n) { std::cout << "int" << std::endl; }
void foo(double d) { std::cout << "double" << std::endl; }
void foo(bool b) { std::cout << "bool" << std::endl; }
void foo(char* s) { std::cout << "char*" << std::endl; }
int main()
{
foo(0); // error
}
main.cpp:10:10: error: call of overloaded ‘foo(int)’ is ambiguous
그런데 만약 foo 함수의 double, bool, char* 인자를 받는 함수들이 모두 있는게 아닌 하나의 함수가 존재한다면, 정상적으로 호출되는 것을 알 수 있다. 모호하지 않고 단 하나를 선택할 수 있기 때문이다.
#include <iostream>
//void foo(int n) { std::cout << "int" << std::endl; }
//void foo(double d) { std::cout << "double" << std::endl; }
void foo(bool b) { std::cout << "bool" << std::endl; }
//void foo(char* s) { std::cout << "char*" << std::endl; }
int main()
{
foo(0); // bool
}
명확하게 실수 (0.0) 을 호출할 때는 double형이 실행됨을 확인할 수 있다.
#include <iostream>
void foo(int n) { std::cout << "int" << std::endl; }
void foo(double d) { std::cout << "double" << std::endl; }
void foo(bool b) { std::cout << "bool" << std::endl; }
void foo(char* s) { std::cout << "char*" << std::endl; }
int main()
{
foo(0.0); // 실수 리터럴
}
char*를 호출하고자 할때는 (char*)0 와 같이 캐스팅을 통한 호출을 해야했다.
nullptr은 포인터 리터럴로 활용할 수 있고 char*가 호출됨을 아래와 같이 확인할 수 있다.
#include <iostream>
//void foo(int n) { std::cout << "int" << std::endl; }
void foo(double d) { std::cout << "double" << std::endl; }
void foo(bool b) { std::cout << "bool" << std::endl; }
void foo(char* s) { std::cout << "char*" << std::endl; }
int main()
{
//foo(0); // int 로 인식된다.
//foo(0.0); // 실수 리터럴
//foo(false); // bool
//foo( (char*)0 ); // char*, 포인터 리터럴이 필요했는데...
foo( nullptr ); // char*, 포인터 리터럴로 nullptr을 활용할 수 있다.
}
ret 값이 auto로 되어 있어 코드를 봤을 때 if ( ret==0) 가 정확하게 무엇과의 비교인지 알기가 어렵다.
가독성을 위해 nullptr로 비교하는 것을 추천한다.
int* foo() { return 0; }
int main()
{
auto ret = foo();
//if ( ret == 0 ) // 0 을 사용하면 이게 int인지 int* 인지... 알기가 어렵다. (가독성 저하!)
if ( ret == nullptr ) // 이처럼 명확하게 nullptr을 사용하기를 추천한다.!!!
{
//...
}
}
nullptr을 사용할 수 있는 경우가 한정되어 있다.
- 포인터 형태에는 nullptr이 초기화가 가능하다.
- int 에는 사용이 불가하며 0로 초기화가 가능하다.
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
}
main.cpp:11:14: error: cannot convert ‘std::nullptr_t’ to ‘int’ in initialization
main.cpp:14:15: error: converting to ‘bool’ from ‘std::nullptr_t’ requires direct-initialization [-fpermissive]
main.cpp:17:23: error: converting to ‘bool’ from ‘std::nullptr_t’ requires direct-initialization [-fpermissive]
NULL과 nullptr의 차이?
NULL과 nullptr은 다르다. c++에서는 nullptr를 사용하기를 권장한다.
NULL이 c++에서는 0으로 취급된다.
C에서는 void* -> 다른 타입으로의 암시적 변환 허용O
C++에서는 void* -> 다른 타입으로의 암시적 변환 허용X
#include <iostream>
void foo(int n) { std::cout << "int" << std::endl; }
void foo(void* p) { std::cout << "void*" << std::endl; }
void goo(char* n) { std::cout << "goo" << std::endl; }
// c++컴파일러에 대부분 이러한 방식으로 되어 있다.
/*
#ifdef __cplusplus
#define NULL 0
#else
#define NULL (void*)0
#endif
*/
int main()
{
foo(0); // int
foo((void*)0); // void* : c++11 이전에 사용법
foo(NULL); // int ?
goo(NULL); // ok
}
NULL이 int 로 취급이 되기 때문에 의심없이 foo(int)로 호출될 것이라 생각했는데,,, 모호하다고 error가 나온다!
'C++' 카테고리의 다른 글
[C++] STL : stack, vector (0) | 2021.08.17 |
---|---|
[C++] 객체 지향 프로그래밍 OOP (0) | 2021.08.16 |
[C++] Explicit Casting (명시적 캐스팅) (0) | 2021.08.14 |
[C++] const reference, return by reference (0) | 2021.08.14 |
[C++] reference 개념 (0) | 2021.08.13 |
댓글