C ++에서 언제 새 키워드를 사용해야합니까?


272

나는 잠시 동안 C ++을 사용하고 있으며 새로운 키워드 에 대해 궁금해하고 있습니다. 간단히 말해서 사용해야합니까?

1) 새로운 키워드로 ...

MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";

2) 키워드가 없으면 ...

MyClass myClass;
myClass.MyField = "Hello world!";

구현의 관점에서, 그들은 다른 것처럼 보이지는 않지만 (그러나 나는 그들이 확실합니다) ... 그러나, 나의 주요 언어는 C #이며, 물론 첫 번째 방법은 내가 익숙한 것입니다.

어려움 1은 방법 1이 std C ++ 클래스와 함께 사용하기 어렵다는 것 같습니다.

어떤 방법을 사용해야합니까?

업데이트 1 :

나는 최근 에 범위를 벗어난 (즉 함수에서 반환되는) 큰 배열 에 메모리 (또는 free store )에 새로운 키워드를 사용했습니다 . 스택을 사용하기 전에 요소의 절반이 범위 밖에서 손상되어 힙 사용으로 전환하면 요소가 제대로 유지되었습니다. 예이!

업데이트 2 :

내 친구가 최근에 new키워드 를 사용하는 간단한 규칙이 있다고 말했습니다 . 입력 할 때마다을 new입력하십시오 delete.

Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.

이렇게하면 삭제를 항상 어딘가에 배치해야하므로 (예를 들어 잘라서 소멸자 또는 다른 방식으로 붙여 넣을 때) 메모리 누수를 방지 할 수 있습니다.


6
짧은 대답은, 당신이 그것을 멀리 갈 수있을 때 짧은 버전을 사용하는 것입니다. :)
jalf

11
사용 STL 컨테이너와 같은 스마트 포인터 - 항상 해당 삭제 쓰기보다 더 나은 기술 std::vectorstd::shared_ptr. 이것들은 당신 new과 의 통화를 랩핑 delete하므로 메모리 누수 가능성이 줄어 듭니다. 예를 들어, 스스로에게 물어보십시오 : delete예외가 발생할 수있는 모든 곳에 해당하는 것을 항상 기억 하는가? delete손 으로 s를 넣는 것은 생각보다 어렵습니다.
AshleysBrain

@nbolton 재 : UPDATE 1 - C ++에 대한 아름다운 것들 중 하나는 C #을 같은 쓰레기 수집 랭 가문 반면에 당신이 스택에 사용자 정의 형식을 저장할 수 있다는 것입니다 힘은 당신이에 데이터를 저장하는 . 힙에 데이터를 저장하면 스택에 데이터를 저장하는 것보다 더 많은 리소스가 소비 되므로 UDT에 데이터를 저장하기 위해 많은 양의 메모리가 필요한 경우를 제외하고 는 스택힙보다 선호해야합니다 . (이것은 또한 기본적으로 객체가 값으로 전달됨을 의미합니다). 문제에 대한 더 나은 해결책 은 배열을 참조로 함수에 전달하는 것 입니다.
Charles Addis

답변:


303

방법 1 (사용 new )

  • 사용 가능한 저장소 의 오브젝트에 메모리를 할당합니다 (이것은 종종 과 동일합니다 )
  • delete나중에 개체 를 명시 적으로 지정 해야합니다. (삭제하지 않으면 메모리 누수가 발생할 수 있습니다)
  • 메모리는 할당 될 때까지 할당 된 상태를 유지 delete합니다. (즉,을 return사용하여 만든 객체 일 수 있음 new)
  • 포인터가 d 가 아니면 문제의 예에서 메모리누출됩니다delete . 그것은 항상 삭제해야 관계없이 수행되는 제어 경로, 또는 예외가 발생하는 경우.

방법 2 (사용하지 않음 new)

  • 스택 에 객체에 대한 메모리 할당 (모든 로컬 변수가있는 경우) 일반적으로 스택에 사용 가능한 메모리가 적습니다. 너무 많은 객체를 할당하면 스택 오버플로가 발생할 위험이 있습니다.
  • delete나중에 필요하지 않습니다 .
  • 메모리가 범위를 벗어나면 더 이상 할당되지 않습니다. (즉 return, 스택의 객체에 대한 포인터 가 아니어야 함 )

어느 것을 사용해야하는지; 위의 제약 조건에 따라 가장 적합한 방법을 선택하십시오.

몇 가지 쉬운 경우 :

  • 호출 delete(및 메모리 누수 의 가능성)에 대해 걱정하지 않으려면 사용하지 않아야합니다 new.
  • 함수에서 객체에 대한 포인터를 반환하려면 new

4
하나의 nitpick-새로운 연산자는 "무료 저장소"에서 메모리를 할당하고 malloc은 "힙"에서 할당한다고 생각합니다. 실제로는 동일하지만 이것들이 동일한 것은 아닙니다. gotw.ca/gotw/009.htm을 참조하십시오 .
Fred Larson

4
귀하의 답변이 더 명확하게 사용될 수 있다고 생각합니다. (시간의 99 %, 선택은 간단합니다. 생성자 / 소멸자에서 new / delete를 호출하는 랩퍼 오브젝트에서 메소드 2를 사용하십시오)
jalf

4
@jalf : 방법 2는 새로운 방법을 사용하지 않는 방법입니다 :-/ 어쨌든, 방법 2 (새로운 방법이없는 방법)를 사용하여 코딩하는 것이 훨씬 간단합니다 (예 : 오류 사례 처리)
Daniel LeCheminant 2018 년

또 다른 이쑤시개 ... Nick의 첫 번째 예제는 메모리가 누출되는 반면, 두 번째 예제는 예외에 직면하더라도 메모리가 누수되지 않는다는 것을 더 분명히해야합니다.
Arafangion

4
@Fred, Arafangion : 통찰력을 가져 주셔서 감사합니다. 귀하의 의견을 답변에 포함 시켰습니다.
Daniel LeCheminant 2016 년

118

둘 사이에는 중요한 차이점이 있습니다.

모든 것은 할당하지 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제대로 수행하고 올바르게 호출됩니다. 그러나 여전히 매우 중요한 규칙입니다. )


2
"새로 할당되지 않은 모든 것은 스택에 배치됩니다." 내가 작업 한 시스템이 아니라 일반적으로 초기화 된 (및 초기화되지 않은) 전역 (정적) 데이터는 자체 세그먼트에 배치됩니다. 예를 들어 .data, .bss 등 ... 링커 세그먼트입니다. Pedantic, 알아요 ...
Dan

물론 그렇습니다. 나는 정적 데이터에 대해 실제로 생각하지 않았습니다. 물론 나쁘다. :)
jalf

2
스택에 할당 된 항목의 크기가 일정한 이유는 무엇입니까?
user541686

그것은하지 않습니다 항상 그것을 회피 할 수있는 몇 가지 방법이 있습니다,하지만 스택에 있기 때문에 일반적인 경우에는 않습니다. 스택의 맨 위에 있으면 크기를 조정할 수 있지만 일단 그 위에 무언가를 밀어 넣으면 양쪽에있는 물체로 둘러싸인 "벽에 쌓여"있으므로 실제로 크기를 조정할 수 없습니다 . 예, 항상 고정 된 크기가 단순화의 조금이다 가지고 있지만, 기본적인 아이디어를 전달 (그리고 난 당신이 스택 할당 너무 창조하자 C의 기능에 대해 장난 권하고 싶지 않다)
jalf을

14

어떤 방법을 사용해야합니까?

이것은 타이핑 환경 설정에 따라 결정되는 것이 아니라 컨텍스트에 따라 결정됩니다. 객체를 몇 개의 스택에 유지해야하거나 스택에 비해 너무 무거 우면 무료 저장소에 할당하십시오. 또한 개체를 할당하기 때문에 메모리를 해제해야합니다. 조회delete운영자를 .

자유 매장 관리 사람들 사용의 부담을 완화하기 위해 같은 물건을 발명 한 auto_ptrunique_ptr. 이것들을 살펴 보는 것이 좋습니다. 입력 문제에 도움이 될 수도 있습니다. ;-)


10

C ++로 작성하는 경우 성능을 위해 작성 중일 수 있습니다. 새로운 저장소와 무료 저장소를 사용하면 스택을 사용하는 것보다 (특히 스레드를 사용할 때) 속도가 훨씬 느리므로 필요할 때만 사용하십시오.

다른 사람들이 말했듯이 객체가 함수 또는 객체 범위 외부에 있어야 할 때, 객체가 실제로 크거나 컴파일 타임에 배열의 크기를 모르는 경우 새 것이 필요합니다.

또한 삭제를 사용하지 마십시오. 대신 새로운 것을 스마트 포인터로 감싸십시오. 스마트 포인터 호출을 통해 삭제하십시오.

스마트 포인터가 똑똑하지 않은 경우가 있습니다. STL 컨테이너 안에 std :: auto_ptr <>을 저장하지 마십시오. 컨테이너 내부의 복사 작업으로 인해 포인터가 너무 빨리 삭제됩니다. 또 다른 경우는 객체에 대한 포인터의 STL 컨테이너가 실제로 큰 경우입니다. boost :: shared_ptr <>은 참조 카운트를 올리거나 내릴 때 엄청난 속도 오버 헤드를 갖습니다. 이 경우 더 좋은 방법은 STL 컨테이너를 다른 객체에 넣고 해당 객체에 컨테이너의 모든 포인터에서 delete를 호출하는 소멸자를 제공하는 것입니다.


10

짧은 대답은 : 당신이 C ++에서 초보자라면, 당신이해야 결코 사용되지 newdelete자신.

대신 std::unique_ptrand std::make_unique(또는 덜 자주 std::shared_ptrstd::make_shared) 와 같은 스마트 포인터를 사용해야합니다 . 그렇게하면 메모리 누수에 대해 거의 걱정할 필요가 없습니다. 당신이 더 진보 된 경우에도 그리고, 가장 좋은 방법은 일반적으로 사용하고있는 사용자 정의 방식을 캡슐화하는 것 newdelete 단지 객체의 라이프 사이클 문제에 전념하고 있습니다 (예 : 사용자 정의 스마트 포인터로) 작은 클래스로합니다.

물론 배후에서 이러한 스마트 포인터는 여전히 동적 할당 및 할당 해제를 수행하므로이를 사용하는 코드에는 여전히 관련 런타임 오버 헤드가 있습니다. 여기에있는 다른 답변은 이러한 문제와 스마트 포인터를 사용하는 시점과 스택에서 객체를 만들거나 객체를 직접 멤버로 통합하는 것과 비교할 때 디자인 결정을 내리는 방법에 대해 설명하지 않았습니다. 그러나 내 간부 요약은 무언가 강요 될 때까지 스마트 포인터 또는 동적 할당을 사용하지 마십시오.


흥미로운 시간이 지남에 따라 답이 변경 될 수 있습니다 방식을 볼 수 있습니다)
늑대


2

간단한 대답은 예입니다-new ()는 힙에 객체를 생성합니다 (불명 한 부작용으로 명시 적으로 delete를 호출하여 수명을 관리해야 함). 두 번째 양식은 현재 스택의 객체를 생성합니다 범위와 해당 개체가 범위를 벗어나면 소멸됩니다.


1

변수가 단일 함수의 컨텍스트 내에서만 사용되는 경우 스택 변수, 즉 옵션 2를 사용하는 것이 좋습니다. 다른 사람들이 말했듯이 스택 변수의 수명을 관리 할 필요는 없습니다. 자동으로 파괴됩니다. 또한 힙에 변수를 할당 / 할당 해제하는 데 비해 속도가 느립니다. 함수가 자주 호출되면 스택 변수와 힙 변수를 사용하면 성능이 크게 향상됩니다.

즉, 스택 변수가 부족한 몇 가지 명백한 사례가 있습니다.

스택 변수의 메모리 풋 프린트가 크면 스택 오버 플로우의 위험이 있습니다. 기본적 으로 Windows 에서 각 스레드의 스택 크기는 1MB 입니다. 크기가 1MB 인 스택 변수를 생성 할 가능성은 없지만 스택 사용률은 누적됨을 명심해야합니다. 함수가 다른 함수를 호출하는 다른 함수를 호출하는 함수를 호출하면 ...,이 모든 함수의 스택 변수는 동일한 스택에서 공간을 차지합니다. 재귀의 깊이에 따라 재귀 함수가이 문제를 빠르게 해결할 수 있습니다. 이것이 문제이면 스택 크기를 늘리거나 (권장하지 않음) 새 연산자를 사용하여 힙에 변수를 할당 할 수 있습니다 (권장).

다른 가능성이 높은 조건은 변수가 함수 범위를 벗어나 "살아야"한다는 것입니다. 이 경우 힙에 변수를 할당하여 주어진 함수의 범위를 벗어나는 변수에 도달 할 수 있습니다.


1

함수에서 myClass를 전달하거나 함수 외부에 존재할 것으로 예상합니까? 다른 사람들이 말했듯이 힙에 할당하지 않으면 범위가 중요합니다. 함수를 떠나면 결국 사라집니다. 초보자가 범한 일반적인 실수 중 하나는 함수에 일부 클래스의 로컬 객체를 만들어 힙에 할당하지 않고 반환하는 것입니다. c ++을 수행하는 초기 시절에 이런 종류의 디버깅을 기억할 수 있습니다.


0

두 번째 방법은 선언 int된 것과 함수에 전달되는 매개 변수 목록 과 함께 스택에 인스턴스를 만듭니다 .

첫 번째 방법은 스택 의 포인터 를 위한 공간을 마련하는데 , 메모리 또는 메모리 MyClass저장소에서 힙 또는 무료 저장소에 할당 된 위치로 설정합니다 .

첫 번째 방법은 또한 당신이 만든 delete것을 요구 new하지만, 두 번째 방법에서는 클래스가 범위를 벗어나면 (일반적으로 다음 닫는 괄호) 클래스가 자동으로 파괴되고 해제됩니다.


-1

짧은 대답은 그렇습니다. "new"키워드는 사용할 때 객체 데이터가 스택과 반대로 힙에 저장되므로 매우 중요합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.