둘 사이에는 중요한 차이점이 있습니다.
모든 것은 할당하지 new
많은 C #에서 값 유형처럼 동작합니다 (그리고 사람들은 그 객체가 아마도 가장 일반적인 / 명백한 경우입니다 스택에 할당 된, 그러나 항상 진실 것을 말한다. 더 정확하게, 할당 된 개체 사용하지 않고 new
있는 자동 스토리지를 duration
할당 된 모든 것이 new
힙에 할당되고 C #의 참조 유형과 정확히 동일하게 이에 대한 포인터가 반환됩니다.
스택에 할당 된 것은 컴파일 타임에 결정 된 일정한 크기를 가져야합니다 (컴파일러는 스택 포인터를 올바르게 설정해야하거나 객체가 다른 클래스의 멤버 인 경우 다른 클래스의 크기를 조정해야합니다) . 이것이 C #의 배열이 참조 유형 인 이유입니다. 참조 유형을 사용하면 런타임에 요청해야 할 메모리 양을 결정할 수 있기 때문입니다. 그리고 여기에도 동일하게 적용됩니다. 일정한 크기 (컴파일 타임에 결정될 수있는 크기)를 가진 어레이 만 자동 스토리지 기간 (스택에서)으로 할당 될 수 있습니다. 동적 크기 배열은 다음을 호출하여 힙에 할당해야합니다.new
.
(그리고 C #과의 유사성이 멈추는 곳)
이제 스택에 할당 된 모든 항목에 "자동"저장 기간이 있습니다 (실제로 변수를 auto
있지만 다른 저장 유형이 지정되지 않은 경우 기본값으로 사용되므로 키워드가 실제로 사용되지는 않습니다). 에서 오는)
자동 저장 기간은 정확히 소리가 나는 것을 의미하며 변수의 기간은 자동으로 처리됩니다. 반대로 힙에 할당 된 항목은 수동으로 삭제해야합니다. 예를 들면 다음과 같습니다.
void foo() {
bar b;
bar* b2 = new bar();
}
이 함수는 고려할 가치가있는 세 가지 값을 만듭니다.
1 행 에서 스택 b
에 유형 변수 를 선언합니다 bar
(자동 지속 시간).
라인 2 일, 그것은 선언 bar
포인터 b2
스택 (자동 지속 시간)에를, 그리고 새로운 할당를 호출bar
힙에 객체를. (동적 지속 시간)
함수가 반환되면 다음이 발생합니다. 첫째, b2
범위를 벗어납니다 (파괴 순서는 항상 구성 순서와 반대 임). 그러나 b2
포인터 일뿐이므로 아무 일도 일어나지 않으며 차지하는 메모리가 단순히 해제됩니다. 그리고 중요한 것은 그것이 가리키는 메모리 ( bar
힙 의 인스턴스)는 건드리지 않는다는 것입니다. 포인터 만 자동 지속 시간을 가지므로 포인터 만 해제됩니다. 둘째, b
범위를 벗어나므로 자동 지속 시간이 있으므로 소멸자가 호출되고 메모리가 해제됩니다.
그리고 bar
더미에 인스턴스? 아마 여전히 거기에 있습니다. 아무도 그것을 귀찮게하지 않았으므로 메모리가 누출되었습니다.
이 예제에서 우리는 자동 지속 시간을 가진 모든 것이 범위를 벗어날 때 소멸자가 호출되도록 보장 합니다. 유용합니다. 그러나 힙에 할당 된 것은 필요한만큼 지속되며 배열의 경우와 같이 동적으로 크기를 조정할 수 있습니다. 또한 유용합니다. 이를 사용하여 메모리 할당을 관리 할 수 있습니다. Foo 클래스가 생성자의 힙에 일부 메모리를 할당하고 소멸자에서 해당 메모리를 삭제하면 어떻게 될까요? 그런 다음 다시 확보 할 수있는 안전한 메모리 할당을 보장 할 수 있지만 모든 것을 스택에 강제로 저장하는 데 제한이 없습니다.
이것이 대부분의 C ++ 코드 작동 방식과 거의 같습니다. std::vector
예를 들어 표준 라이브러리를보십시오 . 일반적으로 스택에 할당되지만 동적으로 크기를 조정하고 크기를 조정할 수 있습니다. 그리고 필요에 따라 힙에 메모리를 내부적으로 할당하여이를 수행합니다. 클래스의 사용자는 이것을 보지 못하므로 메모리가 누출되거나 할당 한 것을 정리하는 것을 잊을 수 없습니다.
이 원칙을 RAII (Resource Acquisition is Initialization)라고하며 획득 및 해제해야하는 모든 리소스로 확장 할 수 있습니다. (네트워크 소켓, 파일, 데이터베이스 연결, 동기화 잠금). 모든 생성자는 생성자에서 획득하고 소멸자에서 해제 할 수 있으므로 획득 한 모든 자원이 다시 해제됩니다.
일반적으로 상위 수준 코드에서 직접 새로 만들기 / 삭제를 사용하지 마십시오. 항상 메모리를 관리 할 수있는 클래스로 랩핑하여 메모리가 다시 해제되도록하십시오. (예,이 규칙에는 예외가있을 수 있습니다. 특히, 스마트 포인터를 사용하려면 new
직접 호출 하고 포인터를 생성자에게 전달해야합니다. 그러면 포인터가 생성자를 delete
제대로 수행하고 올바르게 호출됩니다. 그러나 여전히 매우 중요한 규칙입니다. )