C ++ 17에서 std :: make_unique를 사용하는 이유는 무엇입니까?


96

내가 이해하는 한, C ++ 14 std::make_unique는 매개 변수 평가 순서가 지정되지 않아 안전하지 않았기 때문에 도입되었습니다 .

f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A

(설명 : 평가가 먼저 원시 포인터에 대한 메모리를 할당 한 다음 호출 g()하고 std::unique_ptr생성 전에 예외가 throw 되면 메모리가 누출됩니다.)

호출 std::make_unique은 호출 순서를 제한하는 방법이므로 안전을 보장합니다.

f(std::make_unique<MyClass>(param), g());             // Syntax B

: 그 이후로, C ++ (17)는 내 질문에, 그래서 여기, 너무 구문의 안전을, 평가 순서를 명확히했다 사용하는 이유가 여전히 존재 std::make_unique이상 std::unique_ptr++ 17 C에서의 생성자는? 몇 가지 예를 들어 줄 수 있습니까?

현재 내가 상상할 수있는 유일한 이유는 MyClass한 번만 입력 할 수 있다는 것입니다 (를 사용하여 다형성에 의존 할 필요가 없다고 가정 std::unique_ptr<Base>(new Derived(param))). 그러나 그것은 매우 약한 이유처럼 보입니다. 특히 생성자가 std::make_unique하는 동안 deleter를 지정할 수없는 경우 에는 더욱 그렇습니다 std::unique_ptr.

그리고 분명히 std::make_unique말하면 , 나는 표준 라이브러리에서 제거하는 것을지지하는 것이 아니라 (적어도 이전 버전과의 호환성을 위해 타당 함을 유지하는 것이 좋습니다)std::unique_ptr


4
그러나 그것은 매우 약한 이유처럼 보입니다 -> 왜 약한 이유입니까? 유형의 코드 중복을 효과적으로 줄입니다. 삭제 자의 경우 사용자 지정 삭제자를 사용할 때 얼마나 자주 사용 std::unique_ptr합니까? 그것은에 대한에 대한 논쟁이 아니다make_unique
llllllllll

2
나는 더이 있다면 있기 때문에 약한 이유라고 std::make_unique처음에, 나는 그 이유 충분히이 생성자를 사용하는 것보다 덜 표현하지 더 구문이다 특히 때 STL에 추가 할 수있을 거라고 생각하지 않습니다
영원한

1
make_unique를 사용하여 C ++ 14에서 만든 프로그램이있는 경우 해당 함수가 stl에서 제거되는 것을 원하지 않습니다. 또는 이전 버전과 호환되도록하려면.
Serge

2
@Serge 좋은 지적이지만 내 질문의 대상과는 약간 다릅니다. 더 명확하게 수정하겠습니다
Eternal

1
@Eternal은 C ++ 표준 라이브러리를 STL로 참조하는 것은 올바르지 않으며 혼란을 야기하므로 참조하지 마십시오. 참조 stackoverflow.com/questions/5205491/…
Marandil

답변:


74

주된 이유가 제거 된 것이 맞습니다. 여전히 새로운 지침을 사용하지 않으며 타이핑 이유가 적습니다 (유형을 반복하거나 단어를 사용할 필요가 없음 new). 분명히 그것들은 강력한 주장은 아니지만 new내 코드에서 볼 수없는 것을 정말 좋아 합니다.

또한 일관성을 잊지 마십시오. make_shared사용 make_unique은 자연스럽고 패턴에 맞기 때문에 반드시 사용하셔야합니다 . 그것은 변화에 다음 사소한 std::make_unique<MyClass>(param)std::make_shared<MyClass>(param)(또는 반대) 구문 A가 훨씬 더 재 작성의 필요합니다.


41
@reggaeguitar 멈춰서 new생각해야합니다 :이 포인터가 얼마나 오래 지속 될까요? 내가 올바르게 처리 했습니까? 예외가있는 경우 모든 것이 올바르게 정리 되었습니까? 그런 질문을하지 않고 시간을 낭비하고 싶지 않습니다.를 사용 new하지 않으면 그 질문을 할 필요가 없습니다.
NathanOliver

5
프로젝트의 모든 소스 파일에 대해 grep을 수행하고 하나의 new. 멋지지 않을까요?
Sebastian Mach

5
"새로운 것을 사용하지 마십시오"지침의 가장 큰 장점은 간단하다는 것입니다. 따라서 함께 일하고있는 경험이 부족한 개발자에게 쉽게 지침을 제공 할 수 있습니다. 나는 처음에 실현,하지만 그 자체의 값을 가지고하지 않았다
영원한

@NathanOliver 실제로 절대적 으로 사용할 필요는 없습니다 std::make_shared. 할당 된 객체가 크고 std::weak_ptr이를 가리키는 -s 가 많은 경우를 상상해보십시오 . 마지막으로 공유하자마자 객체를 삭제하는 것이 좋습니다. 포인터가 파괴되고 작은 공유 영역에서만 살고 있습니다.
Dev Null

1
@NathanOliver 당신은 않을 것입니다. 내가 말하는 것은 std::make_shared stackoverflow.com/a/20895705/8414561 의 단점입니다 . 객체를 저장하는 데 사용 된 메모리는 마지막 std::weak_ptr이 사라질 때까지 해제 할 수 없습니다 (모든 std::shared_ptr-s가 가리키는 경우에도 결과적으로 객체 자체는 이미 파괴되었습니다).
Dev Null

50

make_unique구별 TT[]하고 T[N], unique_ptr(new ...)하지 않습니다.

당신은 쉽게 된 포인터를 전달하여 정의되지 않은 동작을 얻을 수 있습니다 new[]A와 에드을 unique_ptr<T>, 또는 한 포인터를 전달하여 newA를 에드 unique_ptr<T[]>.


더 나쁘다 : 그렇지 않을뿐만 아니라 완전히 할 수 없다.
Deduplicator

22

그 이유는 중복없이 짧은 코드를 가지기 때문입니다. 비교

f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());

MyClass, new및 중괄호를 저장 합니다. ptr 과 비교하여 make 에서 한 문자 만 더 비쌉니다 .


2
글쎄, 내가 질문에서 말했듯이, 나는 단지 한 번만 언급하면 ​​타이핑이 적다는 것을 알 수 MyClass있지만 그것을 사용하는 더 강력한 이유가 있는지 궁금합니다
Eternal

2
대부분의 경우 공제 가이드는 <MyClass>첫 번째 변형 의 부품 을 제거하는 데 도움이됩니다 .
AnT

9
다른 답변에 대한 주석에서 이미 언급되었지만 C ++ 17은 생성자에 대한 템플릿 유형 추론을 도입했지만 std::unique_ptr허용되지 않는 경우에는 . 그것은 구별 std::unique_ptr<T>과 관련이 있습니다std::unique_ptr<T[]>
Eternal

19

모든 사용은 new평생의 정확성을 위해 특별히 신중하게 감사되어야합니다. 삭제 되나요? 한 번만?

의 모든 사용이 make_unique이러한 추가 특성을위한 것은 아닙니다. 소유하는 객체가 "올바른"수명을 갖는 한, 고유 포인터가 "올바른"상태를 유지하도록 재귀 적으로 만듭니다.

이제 unique_ptr<Foo>(new Foo())모든면에서 1 에서 make_unique<Foo>(); " new감사를 위해 모든 용도로 소스 코드를 grep "하면됩니다.


1 사실은 일반적인 경우에 거짓말입니다. 완벽한 전달은 완벽하지 않습니다 {}. 기본 init, 배열은 모두 예외입니다.


기술적으로 unique_ptr<Foo>(new Foo)는 후자와 완전히 동일 make_unique<Foo>()하지는 new Foo()않지만 그렇지 않으면 예.
Barry

@barry true, 오버로드 된 연산자 new가 가능합니다.
Yakk-Adam Nevraumont

@dedup C ++ 17 요술이 무슨 파울인가요?
Yakk-Adam Nevraumont

2
@Deduplicator는 C ++ 17 std::unique_ptr이 허용되지 않는 경우 생성자에 대한 템플릿 유형 추론을 도입 했습니다. 구별 std::unique_ptr<T>과 관련 이 있다면std::unique_ptr<T[]>
Eternal

@ Yakk-AdamNevraumont 나는 새로운 오버로딩을 의미하는 것이 아니라 default-init 대 value-init을 의미했습니다.
Barry

0

그 이후로 C ++ 17은 평가 순서를 명확히하여 구문 A도 안전하게 만들었습니다.

정말 충분하지 않습니다. 안전을 보장하기 위해 최근에 도입 된 기술 조항에 의존하는 것은 매우 강력한 관행이 아닙니다.

  • 누군가이 코드를 C ++ 14로 컴파일 할 수 있습니다.
  • new예를 들어 예제를 복사하여 붙여 넣는 등 다른 곳에서 raw의 사용을 장려 할 수 있습니다 .
  • SM이 제안했듯이 코드 중복이 있기 때문에 한 유형은 다른 유형이 변경되지 않고 변경 될 수 있습니다.
  • 어떤 종류의 자동 IDE 리팩토링은 그것을 new다른 곳으로 옮길 수 있습니다 .

일반적으로 언어 레이아웃에 의존 하지 않고 표준에서 사소하거나 모호한 기술 조항을 찾지 않고 코드가 적절하고 / 견고하며 / 명확하게 유효한 것은 좋은 생각입니다 .

(이것은 본질적 으로 튜플 파괴의 순서에 대해 여기에서 만든 것과 동일한 주장 입니다.)


-1

void function (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...} 고려

new A ()가 성공했지만 new B ()가 예외를 던진다 고 가정 해보자 : 프로그램의 정상적인 실행을 재개하기 위해이를 포착한다. 불행히도 C ++ 표준은 객체 A가 파괴되고 메모리가 할당 해제 될 것을 요구하지 않습니다. 메모리가 자동으로 누출되고이를 정리할 방법이 없습니다. A와 B를 std :: make_uniques로 래핑하면 누출이 발생하지 않습니다.

void function (std :: make_unique (), std :: make_unique ()) {...} 여기서 요점은 std :: make_unique 및 std :: make_unique가 이제 임시 객체이며 임시 객체 정리가 C ++ 표준 : 소멸자가 트리거되고 메모리가 해제됩니다. 따라서 가능하다면 항상 std :: make_unique 및 std :: make_shared를 사용하여 객체를 할당하는 것을 선호합니다.


4
저자는 C ++ 17에서 더 이상 누출이 발생하지 않을 것이라고 명시 적으로 명시했습니다. "그 이후로 C ++ 17은 평가 순서를 명확히하여 구문 A도 안전하게 만들었습니다. 그래서 여기에 제 질문이 있습니다 : (...)". 당신은 그의 질문에 대답하지 않았습니다.
R2RT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.