일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- virtual function
- c++ basic practice
- vector size
- operator overloading
- dynamic_cast
- diamond inheritance
- conversion constructor
- std::cout
- virtual destructor
- placement new
- discord bot
- constructor
- virtual function table
- virtual inheritance
- suffix return type
- std::endl
- C++
- new&delete
- std::ostream
- return by reference
- increment operator
- vector capacity
- member function pointer
- this call
- 더 지니어스 양면포커
- std::vector
- delete function
- base from member
- pointer to member data
- c++ multi chatting room
- Today
- Total
I'm FanJae.
[C++] nullptr 본문
1. nullptr
- '0'은 원래 정수(int)형 literal(리터럴)이다.
- 이는 포인터 변수 초기화에 사용될 수 있다.
- 0이 아닌 다른 정수형 literal은 포인터로 암시적 변환이 될 수 없다.
- 0을 가진 정수형 변수도 포인터로 암시적 형변환이 될 수 없다.
int n1 = 0;
int* p1 = 0;
int* p2 = n1; // error
nullptr // C++11
- null pointer를 나타내는 literal
- 모든 종류(타입)의 포인터 변수를 초기화 하는데 사용 가능
- 정수(실수) 초기화에 사용될 수는 없다.
int* p4 = nullptr;
int n2 = nullptr; // error
1-1. nullptr의 등장배경
- 컴파일러마다 조금 차이는 있지만, 일반적으로 C와 C++에서 NULL의 매크로 정의하는 방법은 아래와 같다.
#ifdef __cplusplus
#define NULL 0
#else
#define NULL (void *) 0
#endif
- __cplusplus란 모든 C++ 컴파일러가 제공하는 매크로이다.
void foo(int n) { std::cout << "int" << std::endl; }
void foo(void *p) { std::cout << "void*" << std::endl; }
int main(void)
{
foo(0);
foo(NULL); // void *을 기대하였으나, int가 나옴.
}
- C++에서 함수 오버로딩을 지원한다.
- C언어에서의 NULL을 기대하고 void *가 나오길 바랬지만, 기대한대로 나오지 않는다.
[출력 결과]
int
int
- int와 void *가 나오길 기대했지만, C++에서 NULL은 0으로 정의된 상수이다. 0이니까 int로 받아들인 것이다.
- 만약 제대로 된 결과를 기대한다면, 아래와 같이 호출해야 한다.
foo(0)
foo((void *)0);
- 이 상황을 잘 해결하였지만, 또 다른 문제가 존재한다.
void goo(char *p) { std::cout << "char*" << std::endl; }
goo(0); // Ok.
goo((void *)0) // C 언어에서는 문제 없으나, C++에서는 Error
- 위와 같은 함수가 있을때, C언어에서는 void *를 char *로 암시적 형변환 해주었지만, C++에서는 해주지 않는다.
NULL은 포인터 0의 의미로 사용하기 위해 만든 매크로이나, 일반적으로 C 언어와 C++ 언어에서 다르게 정의된다. 이는 컴파일러마다 조금씩 차이가있다.
C 언어 : (void *) 0.
C++ 언어 : 0 또는 0L
※ 따라서, 이들을 NULL이아닌 nullptr로 호출하면 원하는 결과 출력이 가능하다.
void foo(int n) { std::cout << "int" << std::endl; }
void foo(void *p) { std::cout << "void*" << std::endl; }
int main(void)
{
foo(0);
foo(nullptr); // void *
}
void goo(char *p) { std::cout << "char*" << std::endl; }
goo(nullptr); // char*가 나옴.
2. std::nullptr_t
- 모든 리터럴은 타입이 존재한다. (Ex. 0 -> int, 0.0 -> double, false -> bool)
- nullptr은 포인터형 리터럴은 std::nulptr_t이다.
std::nullptr_t
- nullptr의 타입
- 모든 종류(타입)의 포인터로 암시적 형변환이 된다.
#include <iostream>
int main()
{
std::nullptr_t null = nullptr;
int* p1 = null; // Ok.
char* p2 = null; // Ok.
}
- 암시적으로 형변환이 되기 때문에 정상 실행이 된다.
nullptr과 template
template<class F, class T>
void call(F f, T arg) // int arg = 0
{ // std::nullptr_t arg = nullptr
f(arg);
}
void foo(int a) {}
void goo(int *p) {}
int main()
{
foo(10); // ok
goo(0); // ok
call(foo, 10);
call(goo, 0); // error
call(goo,nullptr); // ok
}
- 이 문장에서 call(goo, 0);의 경우 에러가 발생한다.
- call에서 각각 goo,0을 받으면 이를 goo(arg)로 변환하여 넣을 것이다.
- goo(arg)로 변환하였기 때문에, 정수형 변수로 인식하면서, 이를 포인터로 형변환이 되지 않는다.
따라서, 0이 아닌 nullptr을 넣어서 사용해줘야 정상 작동 한다.
※ 결론은 포인터 변수를 초기화 할때는 이제부터 0이 아닌 nullptr을 사용해야 한다.
'C++ > Basic' 카테고리의 다른 글
[C++] OOP(Object Oriented Programming) I (0) | 2024.08.12 |
---|---|
[C++] Explicit casting (0) | 2024.08.11 |
[C++] Reference (0) | 2024.08.09 |
[C++] 연산자와 제어문 (0) | 2024.08.09 |
[C++] Function III (0) | 2024.08.08 |