머리말
자바는 과대 광고와 달리 C ++과는 다릅니다. Java 과장 기계는 Java가 C ++과 같은 구문을 가지고 있기 때문에 언어가 유사하다고 생각합니다. 진실에서 더 이상 갈 수있는 것은 없습니다. 이 잘못된 정보는 Java 프로그래머가 코드의 의미를 이해하지 않고 C ++로 이동하여 Java와 유사한 구문을 사용하는 이유의 일부입니다.
앞으로 우리는 간다
그러나 왜 우리가 이런 식으로 해야하는지 알 수 없습니다. 메모리 주소에 직접 액세스 할 수 있기 때문에 효율성과 속도와 관련이 있다고 가정합니다. 내가 맞아?
반대로, 실제로. 스택은 힙에 비해 매우 단순하므로 힙이 스택보다 훨씬 느립니다 . 자동 저장 변수 (일명 스택 변수)는 소멸자가 범위를 벗어나면 호출됩니다. 예를 들면 다음과 같습니다.
{
std::string s;
}
// s is destroyed here
반면 동적으로 할당 된 포인터를 사용하는 경우 소멸자를 수동으로 호출해야합니다. delete
이 소멸자를 호출합니다.
{
std::string* s = new std::string;
}
delete s; // destructor called
이것은 new
C # 및 Java에서 널리 사용되는 구문 과 관련이 없습니다 . 그것들은 완전히 다른 목적으로 사용됩니다.
동적 할당의 이점
1. 사전에 배열의 크기를 알 필요가 없습니다
많은 C ++ 프로그래머가 겪는 첫 번째 문제 중 하나는 사용자의 임의 입력을 받아 들일 때 스택 변수에 고정 된 크기 만 할당 할 수 있다는 것입니다. 배열의 크기도 변경할 수 없습니다. 예를 들면 다음과 같습니다.
char buffer[100];
std::cin >> buffer;
// bad input = buffer overflow
물론, std::string
대신 사용하면 std::string
내부적으로 크기가 조정되므로 문제가되지 않습니다. 그러나 본질적으로이 문제에 대한 해결책은 동적 할당입니다. 예를 들어, 사용자의 입력을 기반으로 동적 메모리를 할당 할 수 있습니다.
int * pointer;
std::cout << "How many items do you need?";
std::cin >> n;
pointer = new int[n];
참고 : 많은 초보자들이 실수로하는 실수 중 하나는 가변 길이 배열의 사용입니다. 이것은 GNU 확장이며 Clang의 확장이기도합니다. 많은 GCC 확장을 반영하기 때문입니다. 따라서 다음에
int arr[n]
의존해서는 안됩니다.
힙이 스택보다 훨씬 크기 때문에 필요한만큼의 메모리를 임의로 할당 / 재 할당 할 수 있지만 스택에는 제한이 있습니다.
2. 배열은 포인터가 아니다
이것이 당신이 요구하는 이점은 무엇입니까? 배열과 포인터의 혼란과 신화를 이해하면 대답이 명확해질 것입니다. 일반적으로 동일한 것으로 가정하지만 그렇지 않습니다. 이 신화는 포인터가 배열처럼 첨자 화 될 수 있고 함수 선언에서 최상위 레벨의 포인터로 배열이 소멸 될 수 있다는 사실에서 비롯됩니다. 그러나 배열이 포인터로 붕괴되면 포인터가 sizeof
정보를 잃게 됩니다. 따라서 sizeof(pointer)
64 비트 시스템에서 일반적으로 8 바이트 인 포인터 크기를 바이트 단위로 제공합니다.
배열은 할당 할 수 없으며 초기화 만합니다. 예를 들면 다음과 같습니다.
int arr[5] = {1, 2, 3, 4, 5}; // initialization
int arr[] = {1, 2, 3, 4, 5}; // The standard dictates that the size of the array
// be given by the amount of members in the initializer
arr = { 1, 2, 3, 4, 5 }; // ERROR
반면에 포인터로 원하는 것을 할 수 있습니다. 불행히도, 포인터와 배열의 구별은 Java와 C #에서 수동으로 이루어지기 때문에 초보자는 그 차이를 이해하지 못합니다.
3. 다형성
Java 및 C #에는 as
키워드 를 사용하여 객체를 다른 객체로 취급 할 수있는 기능이 있습니다 . 누군가가 치료 싶어한다면 Entity
A와 객체를 Player
객체, 사람은 할 수있는 Player player = Entity as Player;
경우에만 특정 유형에 적용해야 균일 한 용기에 함수를 호출하려는 경우에 매우 유용합니다. 기능은 아래와 비슷한 방식으로 달성 될 수 있습니다.
std::vector<Base*> vector;
vector.push_back(&square);
vector.push_back(&triangle);
for (auto& e : vector)
{
auto test = dynamic_cast<Triangle*>(e); // I only care about triangles
if (!test) // not a triangle
e.GenericFunction();
else
e.TriangleOnlyMagic();
}
따라서 Triangles에만 Rotate 함수가 있으면 클래스의 모든 객체에서 호출하려고하면 컴파일러 오류가 발생합니다. 를 사용 dynamic_cast
하면 as
키워드를 시뮬레이션 할 수 있습니다 . 명확하게 말하면, 캐스트가 실패하면 유효하지 않은 포인터를 반환합니다. 따라서 !test
본질적으로 test
NULL 또는 유효하지 않은 포인터인지 확인하기위한 약칭으로 , 캐스트가 실패했음을 의미합니다.
자동 변수의 장점
동적 할당이 할 수있는 모든 위대한 일을보고 나면 왜 동적 할당을 항상 사용하지 않는 사람이 있는지 궁금 할 것입니다. 나는 이미 한 가지 이유를 말했지만 힙이 느립니다. 모든 메모리가 필요하지 않은 경우 남용해서는 안됩니다. 따라서 특별한 순서가없는 몇 가지 단점이 있습니다.
오류가 발생하기 쉽습니다. 수동 메모리 할당은 위험하며 누수가 발생하기 쉽습니다. 디버거 나 valgrind
(메모리 누수 도구) 사용에 능숙하지 않으면 머리카락을 머리에서 빼낼 수 있습니다. 운 좋게도 RAII 관용구와 똑똑한 포인터는 이것을 조금 완화하지만, Rule of Three와 The Rule Of Five와 같은 관행에 익숙해야합니다. 많은 정보가 필요하며, 모르거나 신경 쓰지 않는 초보자는이 함정에 빠질 것입니다.
필요하지 않습니다. new
모든 곳 에서 키워드 를 사용하는 것이 관용 인 Java 및 C #과 달리 C ++에서는 필요한 경우에만 사용해야합니다. 일반적인 문구는 망치가 있으면 모든 것이 못처럼 보입니다. C ++로 시작하는 초보자는 포인터가 무섭고 습관적으로 스택 변수를 사용하는 법을 배우는 반면 Java 및 C # 프로그래머는 포인터를 이해하지 않고 포인터를 사용하여 시작 합니다! 말 그대로 잘못된 발을 밟고 있습니다. 구문은 하나이고 언어를 배우는 것은 다른 것이므로 아는 모든 것을 버려야합니다.
1. (N) RVO-일명, (명명 된) 반환 값 최적화
많은 컴파일러가 만드는 최적화 중 하나는 elision 및 return value optimization 입니다. 이러한 것들은 많은 요소를 포함하는 벡터와 같이 매우 큰 객체에 유용한 불필요한 사본을 피할 수 있습니다. 일반적으로 큰 개체를 복사하여 이동 하지 않고 포인터를 사용하여 소유권 을 이전 하는 것이 일반적 입니다. 이것은 이동 의미론 과 스마트 포인터 의 시작으로 이어졌다 .
포인터를 사용하는 경우 (N) RVO가 발생 하지 않습니다 . 최적화가 걱정되면 포인터를 반환하거나 전달하는 것보다 (N) RVO를 이용하는 것이 더 유리하고 오류가 적은 경향이 있습니다. 함수의 호출자가 delete
동적으로 할당 된 객체 등을 담당하는 경우 오류가 발생할 수 있습니다 . 포인터가 핫 포테이토처럼지나 가면 객체의 소유권을 추적하기가 어려울 수 있습니다. 스택 변수는 더 단순하고 우수하므로 사용하십시오.