std::unique_ptr
예를 들어 배열을 지원합니다.
std::unique_ptr<int[]> p(new int[10]);
그러나 필요합니까? 아마도 사용하는 것이 더 편리하다 std::vector
나 std::array
.
그 구조에 대한 사용을 찾으십니까?
std::shared_ptr
<T []>는 이제 C ++ 17에 있습니다.
std::unique_ptr
예를 들어 배열을 지원합니다.
std::unique_ptr<int[]> p(new int[10]);
그러나 필요합니까? 아마도 사용하는 것이 더 편리하다 std::vector
나 std::array
.
그 구조에 대한 사용을 찾으십니까?
std::shared_ptr
<T []>는 이제 C ++ 17에 있습니다.
답변:
일부 사람들은 std::vector
할당자를 사용 하더라도 고급 기능을 사용할 수 없습니다 . 어떤 사람들은 동적 크기의 배열을 필요로 std::array
합니다. 그리고 어떤 사람들은 배열을 반환하는 것으로 알려진 다른 코드에서 배열을 얻습니다. 그 코드는 vector
또는 다른 것을 반환하기 위해 다시 작성되지 않습니다 .
을 허용 unique_ptr<T[]>
하면 해당 요구 를 처리 할 수 있습니다.
요컨대, 필요할unique_ptr<T[]>
때 사용 합니다. 대안이 단순히 효과가 없을 때. 최후의 수단입니다.
vector
". 이러한 요구 사항이 합당한 요구 사항인지 아닌지를 주장 할 수는 있지만 요구 사항 이 존재 한다는 것을 부인할 수는 없습니다 .
std::vector
있다면 누군가가 사용할 수없는 이유는 없습니다 std::unique_ptr
.
unique_ptr
둘 중 하나를 사용 하지는 않지만 그러한 종류의 프로젝트는 실제로 존재합니다.
트레이드 오프가 있으며 원하는 것과 일치하는 솔루션을 선택합니다. 내 머리 꼭대기에서 :
초기 크기
vector
그리고 unique_ptr<T[]>
크기는 실행 시간에 지정할 수 있습니다array
컴파일시 크기 만 지정할 수 있습니다.크기 조정
array
및 unique_ptr<T[]>
크기 조정 허용하지 않습니다vector
않습니다저장
vector
및 unique_ptr<T[]>
(일반적 힙) 객체 외부에 데이터를 저장할array
객체에 데이터를 직접 저장사자
array
그리고 vector
복사를 허용unique_ptr<T[]>
복사를 허용하지 않습니다스왑 / 이동
vector
및 unique_ptr<T[]>
O (1)이 시간 swap
및 이동 동작을array
O (n) 시간 swap
및 이동 연산이 있으며 여기서 n은 배열의 요소 수입니다.포인터 / 참조 / 반복자 무효화
array
객체가 활성화되어있는 동안에도 포인터, 참조 및 반복자가 무효화되지 않도록합니다. swap()
unique_ptr<T[]>
반복자가 없습니다. 포인터와 참조는 swap()
객체가 활성화되어있는 동안에 만 무효화됩니다 . 스와핑 한 후에는 포인터가 스왑 한 배열을 가리 키므로 여전히 그런 의미에서 "유효"합니다.vector
재 할당에서 포인터, 참조 및 반복자를 무효화 할 수 있습니다 (재 할당이 특정 작업에서만 발생할 수 있음을 보장합니다).개념 및 알고리즘과의 호환성
array
그리고 vector
둘 다 컨테이너입니다unique_ptr<T[]>
컨테이너가 아닙니다정책 기반 설계로 리팩토링 할 수있는 기회 인 것 같습니다.
vector
. 그런 다음 vector
재 할당 하도록 크기 나 용량을 늘 립니다. 그런 다음 반복자, 포인터 또는 참조가 더 이상의 해당 요소를 가리 키지 않습니다 vector
. 이것이 "무효"라는 의미입니다. array
"재 할당"이 없기 때문에이 문제가 발생하지 않습니다 . 실제로, 나는 방금 그것에 대한 세부 사항을 보았고 그것을 맞게 편집했습니다.
unique_ptr<T[]>
재 할당이 없기 때문에 무효화 할 수 없습니다 . 그러나 물론 배열이 범위를 벗어날 때 특정 요소에 대한 포인터는 여전히 유효하지 않습니다.
T[]
크기 (또는 동등한 정보)가 어딘가에 매달려 있어야합니다 operator delete[]
. 프로그래머가 액세스 할 수 있다면 좋을 것입니다.
a를 사용하는 한 가지 이유 는 배열 unique_ptr
의 값을 초기화 하는 런타임 비용을 지불하고 싶지 않기 때문 입니다.
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars
std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
std::vector
생성자와 std::vector::resize()
초기화 값 것이다 T
- 그러나 new
경우는 그렇게하지 않을 것이다 T
포드입니다.
C ++ 11 및 std :: vector 생성자의 Value-Initialized Objects 참조
참고 vector::reserve
여기에 대안이 아니다는 : 표준 : : 벡터 후에 원시 포인터에 액세스 :: 예비 안전?
그것은 C 프로그래머가 선택할 수 같은 이유 malloc
이상 calloc
.
std::unique_ptr
많은 std::vector
구현 과 달리 바운드 검사를 제공하지 않습니다 .
std::vector
에서 경계를 검사하기 위해 표준에 필요하다 .at()
. 일부 구현에는 체크인 할 디버그 모드가 .operator[]
있지만 좋은 휴대용 코드를 작성하는 데 쓸모없는 것으로 생각합니다.
는 std::vector
동안, 주변에 복사 할 수 있습니다 unique_ptr<int[]>
배열의 고유 한 소유권을 표현 할 수 있습니다. std::array
반면에, 컴파일 타임에 크기를 결정해야하므로 일부 상황에서는 불가능할 수 있습니다.
unique_ptr
대신 사용 하는 것을 막고 싶을 수도 있습니다 shared_ptr
. 뭔가 빠졌습니까?
unique_ptr
우발적 인 오용을 방지하는 것 이상을 수행합니다. 또한보다 작고 오버 헤드가 낮습니다 shared_ptr
. 요점은 "오용"을 방지하는 클래스에 의미를 갖는 것이 좋지만 특정 유형을 사용해야하는 유일한 이유는 아니라는 점입니다. 그리고 크기 가 있다는 사실 외에 다른 이유가없는 경우 vector
보다 어레이 스토리지로 훨씬 더 유용 합니다 . unique_ptr<T[]>
vector
이상 unique_ptr<T[]>
대신 말하는, "당신이 그것을 복사 할 수 없습니다"따라서 선택 가능한, unique_ptr<T[]>
당신이 사본을하지 않으려는 경우가. 누군가가 잘못한 일을 막는 것이 반드시 수업을 선택하는 가장 중요한 이유는 아닙니다.
std::vector
a보다 오버 헤드가 더 많으며 std::unique_ptr
~ 1 대신 ~ 3 포인터를 사용합니다. std::unique_ptr
복사 구성을 차단하지만 이동 구성을 활성화합니다. 의미 적으로 작업중인 데이터를 이동 만 할 수 있지만 복사 할 수없는 class
경우 데이터 가 포함 된 데이터를 감염시킵니다 . 유효하지 않은 데이터에 대한 조작이 실제로 컨테이너 클래스를 악화시키고 "사용하지 마십시오"라고 모든 죄를 씻어 내지는 않습니다. std::vector
수동으로 비활성화하는 클래스 에 모든 인스턴스를 넣어야 move
하는 것은 골치 아픈 일입니다. std::unique_ptr<std::array>
가 있습니다 size
.
Scott Meyers는 Effective Modern C ++에서 다음과 같이 말합니다.
의 존재
std::unique_ptr
배열이 있기 때문에, 당신 만 지적 관심을해야한다std::array
,std::vector
,std::string
거의 항상 원시 배열보다 더 나은 데이터 구조의 선택입니다. 내가std::unique_ptr<T[]>
이해할 수있는 유일한 상황에 대해서는 소유권이 있다고 가정하는 힙 배열에 대한 원시 포인터를 반환하는 C와 같은 API를 사용하는 경우가 될 것입니다.
Charles Salvia의 대답은 관련이 있다고 생각합니다 std::unique_ptr<T[]>
. 컴파일시 크기를 알 수없는 빈 배열을 초기화하는 유일한 방법입니다. Scott Meyers는 이러한 사용 동기에 대해 무엇을 말해야 std::unique_ptr<T[]>
합니까?
vector
stackoverflow.com/a/24852984/2436175보다 선호하는 가능한 이유도 있습니다 .
일반적인 일부 패턴은 일부 Windows Win32 API 호출 에서 찾을 수 있습니다 std::unique_ptr<T[]>
. 예를 들어 일부 Win32 API를 호출 할 때 출력 버퍼의 크기를 정확히 알지 못하는 경우 (예 : 내부에 데이터를 쓰는 경우) 해당 버퍼) :
// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;
// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;
LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate buffer of specified length
buffer.reset( BYTE[bufferLength] );
//
// Or, in C++14, could use make_unique() instead, e.g.
//
// buffer = std::make_unique<BYTE[]>(bufferLength);
//
//
// Call some Win32 API.
//
// If the size of the buffer (stored in 'bufferLength') is not big enough,
// the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
// in the [in, out] parameter 'bufferLength'.
// In that case, there will be another try in the next loop iteration
// (with the allocation of a bigger buffer).
//
// Else, we'll exit the while loop body, and there will be either a failure
// different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
// and the required information will be available in the buffer.
//
returnCode = ::SomeApiCall(inParam1, inParam2, inParam3,
&bufferLength, // size of output buffer
buffer.get(), // output buffer pointer
&outParam1, &outParam2);
}
if (Failed(returnCode))
{
// Handle failure, or throw exception, etc.
...
}
// All right!
// Do some processing with the returned information...
...
std::vector<char>
이 경우에 사용할 수 있습니다 .
std::unique_ptr<bool[]>
HDF5 라이브러리 (이진 데이터 저장을위한 라이브러리, 과학에서 많이 사용됨)에있는 을 사용해야하는 경우에 직면했습니다 . 일부 컴파일러 (필자의 경우 Visual Studio 2015) 는 압축에std::vector<bool>
신경 쓰지 않는 HDF5와 같은 재앙 인 (모든 바이트에 8 부울을 사용하여) 압축을 제공합니다. 를 사용 std::vector<bool>
하면 HDF5는 압축으로 인해 결국 가비지를 읽었습니다.
std::vector
작동하지 않는 경우 누가 구조를 위해 있었는지 추측 하고 동적 배열을 깨끗하게 할당해야합니까? :-)
요컨대, 메모리 효율성이 가장 뛰어납니다.
A std::string
는 포인터, 길이 및 "짧은 문자열 최적화"버퍼와 함께 제공됩니다. 그러나 내 상황은 거의 항상 비어있는 문자열을 수십만 개의 구조로 저장해야한다는 것입니다. C에서는을 사용 char *
하고 대부분의 경우 null이됩니다. char *
소멸자가 없으며 자체 삭제를 모르는 것을 제외하고 C ++에서도 작동 합니다. 반대로 a std::unique_ptr<char[]>
는 범위를 벗어날 때 자체를 삭제합니다. 빈칸 std::string
은 32 바이트를 차지하지만 빈칸 std::unique_ptr<char[]>
은 8 바이트를 차지합니다.
가장 큰 단점은 줄의 길이를 알고 싶을 때마다 전화해야한다는 strlen
것입니다.
std::unique_ptr<T[]>
지금까지 응답에서 언급되지 않은 을 허용하고 사용하는 한 가지 추가 이유 : 배열 요소 유형을 전달할 수 있습니다.
이는 #include
빌드 성능을 최적화하기 위해 헤더 의 체인 문 을 최소화하려는 경우에 유용 합니다.
예를 들어-
myclass.h :
class ALargeAndComplicatedClassWithLotsOfDependencies;
class MyClass {
...
private:
std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};
myclass.cpp :
#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"
// MyClass implementation goes here
위의 코드 구조를 #include "myclass.h"
사용 MyClass
하면에 필요한 내부 구현 종속성을 포함하지 않고도 누구나 사용할 수 있습니다 MyClass::m_InternalArray
.
m_InternalArray
대신 각각 std::array<ALargeAndComplicatedClassWithLotsOfDependencies>
, 또는 로 선언 된 경우 std::vector<...>
결과는 불완전한 유형의 사용을 시도하며 이는 컴파일 타임 오류입니다.
나는 받아 들여진 대답의 정신에 충분히 동의하지 않습니다. "최후의 수단"? 그것과는 거리가 멀다!
내가 보는 방법은 C와 다른 C ++에 비해 C ++의 가장 강력한 기능 중 하나는 제약 조건을 표현하여 컴파일 타임에 확인하고 실수로 오용되는 것을 방지 할 수 있다는 것입니다. 따라서 구조를 설계 할 때 어떤 작업을 허용 해야하는지 스스로에게 물어보십시오. 다른 모든 사용은 금지되어야하며, 이러한 제한을 정적으로 (컴파일 시간에) 구현하여 잘못 사용하면 컴파일이 실패하는 것이 가장 좋습니다.
따라서 배열이 필요할 때 다음 질문에 대한 대답은 동작을 지정합니다. 2. 스택에 어레이를 할당 할 수 있습니까?
그리고 답을 바탕으로, 이것은 그러한 배열에 가장 적합한 데이터 구조로 간주됩니다.
Dynamic | Runtime static | Static
Stack std::vector unique_ptr<T[]> std::array
Heap std::vector unique_ptr<T[]> unique_ptr<std::array>
그래, 내 생각에는 unique_ptr<std::array>
또한 고려해야 하며, 최후의 수단이 아니다. 알고리즘에 가장 적합한 것을 생각하십시오.
이 모든 것은 데이터 배열에 대한 원시 포인터 ( vector.data()
/ array.data()
/ uniquePtr.get()
) 를 통해 일반 C API와 호환됩니다 .
PS 위의 고려 사항 외에도 소유권 중 하나가 있습니다. std::array
와 std::vector
동시에, 값 의미를 (복사 한 값으로 전달하기위한 네이티브 지원이)가 unique_ptr<T[]>
전용 (시행한다 단일 소유권을) 이동할 수 있습니다. 다른 시나리오에서 유용 할 수 있습니다. 반대로, 일반 정적 배열 ( int[N]
) 및 일반 동적 배열 ( new int[10]
)은 둘 다를 제공하지 않으므로 가능하면 피해야합니다. 대부분의 경우 가능해야합니다. 이것으로 충분하지 않은 경우 일반 동적 배열은 크기를 쿼리 할 수있는 방법을 제공하지 않으므로 메모리 손상 및 보안 허점에 대한 추가 기회가 제공됩니다.
해치의 다른 쪽에서 "잡힌"후 수명이 측정되는 기존 API (창 메시지 또는 스레딩 관련 콜백 매개 변수)를 통해 단일 포인터 만 찌를 때 가능한 가장 올바른 대답 일 수 있습니다. 그러나 이것은 호출 코드와 관련이 없습니다.
unique_ptr<byte[]> data = get_some_data();
threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
data.release());
우리 모두는 우리에게 좋은 일을 원합니다. 다른 때는 C ++입니다.
unique_ptr<char[]>
C의 성능과 C ++의 편리함을 원하는 곳에서 사용할 수 있습니다. 수백만 개 (아직 신뢰할 수없는 경우 수십억 개)의 문자열을 조작해야한다고 생각하십시오. 각각을 개별 string
또는 vector<char>
오브젝트 에 저장 하면 메모리 (힙) 관리 루틴에 재앙이됩니다. 특히 다른 문자열을 여러 번 할당하고 삭제해야하는 경우.
그러나 많은 문자열을 저장하기 위해 단일 버퍼를 할당 할 수 있습니다. char* buffer = (char*)malloc(total_size);
명백한 이유로 마음 에 들지 않을 것 입니다 (명확하지 않은 경우 "스마트 ptr을 사용하는 이유"를 검색하십시오). 당신은 오히려 좋아합니다unique_ptr<char[]> buffer(new char[total_size]);
비유로, 동일한 성능 및 편의성 고려 사항이 비 char
데이터에 적용됩니다 (수백만 개의 벡터 / 행렬 / 개체 고려).
vector<char>
습니까? 대답은 버퍼를 만들 때 0으로 초기화되고을 사용하는 경우에는 초기화되지 않기 때문입니다 unique_ptr<char[]>
. 그러나이 핵심 덩어리는 귀하의 답변에서 누락되었습니다.
new[]
std::vector
예를 들어 부주의 한 프로그래머가 실수로 사본을 소개하지 않도록하기 위해 회사 나 프로젝트에을 사용하는 것에 대한 일반적인 규칙이 있습니다.포인터를 사용하여 롤링하는 것보다 C ++ 컨테이너를 선호하는 것이 일반적 규칙입니다. 일반적인 규칙입니다. 예외가 있습니다. 더있다; 이것들은 단지 예일뿐입니다.
std::shared_ptr<T[]>
지적해야하지만, 누군가가 제안서를 작성하는 데 방해가된다면 C ++ 14에있을 것이고 아마도있을 것입니다. 그동안 항상boost::shared_array
있습니다.