IT박스

새로운 배열 배치는 버퍼에 지정되지 않은 오버 헤드가 필요합니까?

itboxs 2020. 12. 2. 08:15
반응형

새로운 배열 배치는 버퍼에 지정되지 않은 오버 헤드가 필요합니까?


5.3.4 [expr.new]의 C ++ 11 Feb 초안은 다음과 같은 예를 제공합니다.

new(2,f) T[5]의 호출이 발생 operator new[](sizeof(T)*5+y,2,f)합니다.

여기서 x와 y는 배열 할당 오버 헤드를 나타내는 음이 아닌 지정되지 않은 값입니다. new-expression 의 결과는에서 반환 된 값에서이 양만큼 오프셋됩니다 operator new[]. 이 오버 헤드는 라이브러리 함수 및 기타 배치 할당 함수를 참조하는 것을 포함하여 모든 배열 new-expressions에 적용될 수 있습니다 operator new[](std::size_t, void*). 오버 헤드의 양은 new 호출마다 다를 수 있습니다. -예제 종료 ]

이제 다음 예제 코드를 사용하십시오.

void* buffer = malloc(sizeof(std::string) * 10);
std::string* p = ::new (buffer) std::string[10];

위의 인용문에 따르면 두 번째 줄 new (buffer) std::string[10]은 내부적으로 호출합니다 operator new[](sizeof(std::string) * 10 + y, buffer)(개별 std::string개체 를 생성하기 전에 ). 문제는 만약 y > 0이면 미리 할당 된 버퍼가 너무 작다는 것입니다!

그렇다면 어레이 배치 -new를 사용할 때 사전 할당 할 메모리 양을 어떻게 알 수 있습니까?

void* buffer = malloc(sizeof(std::string) * 10 + how_much_additional_space);
std::string* p = ::new (buffer) std::string[10];

아니면 표준 y == 0이이 경우를 보장 합니까? 다시 말하지만, 인용문은 다음과 같습니다.

이 오버 헤드는 라이브러리 함수 및 기타 배치 할당 함수를 참조하는 것을 포함하여 모든 배열 new-expressions에 적용될 수 있습니다 operator new[](std::size_t, void*).


operator new[](std::size_t, void* p)이 질문에 대한 답을 미리 알지 못하면 사용 하지 마십시오 . 대답은 구현 세부 사항이며 컴파일러 / 플랫폼에 따라 변경 될 수 있습니다. 일반적으로 특정 플랫폼에서 안정적입니다. 예를 들어 이것은 Itanium ABI에서 지정한 것 입니다.

이 질문에 대한 답을 모르는 경우 런타임에 확인할 수있는 새로운 배치 배열을 작성하십시오.

inline
void*
operator new[](std::size_t n, void* p, std::size_t limit)
{
    if (n <= limit)
        std::cout << "life is good\n";
    else
        throw std::bad_alloc();
    return p;
}

int main()
{
    alignas(std::string) char buffer[100];
    std::string* p = new(buffer, sizeof(buffer)) std::string[3];
}

n위의 예에서 어레이 크기를 변경하고 검사 y하여 플랫폼을 추론 할 수 있습니다. 들어 내 플랫폼 y 1 개 단어입니다. sizeof (word)는 내가 32 비트 또는 64 비트 아키텍처 용으로 컴파일하는지에 따라 다릅니다.


업데이트 : 몇 가지 논의 후 내 답변이 더 이상 질문에 적용되지 않음을 이해합니다. 여기에 남겨 두겠습니다.하지만 여전히 진정한 답이 필요합니다.

곧 좋은 답변을 찾지 못하면 바운티로이 질문을 지원해 드리겠습니다.

더 짧은 버전이 다른 사람들이 질문을 이해하는 데 도움이되기를 바라면서 내가 이해하는 한 여기서 질문을 다시 언급하겠습니다. 질문은 ~이야:

다음 구성은 항상 정확합니까? arr == addr마지막에?

void * addr = std::malloc(N * sizeof(T));
T * arr = ::new (addr) T[N];                // #1

우리는 표준에서 # 1이 호출을 유발한다는 것을 알고 있으며 ::operator new[](???, addr), 여기서는 ???보다 작지 않은 지정되지 않은 숫자이며 N * sizeof(T), 해당 호출은 반환 addr만하고 다른 효과는 없다는 것도 알고 있습니다. 우리는 또한 그에 상응하는 arr오프셋 임을 알고 addr있습니다. 우리가 할 수 없습니다 알고하면 메모리가가 가리키는 여부 addr충분히 큰 경우, 또는 우리가 할당하는 메모리 양을 알게 될 것입니다 방법에 대해 설명합니다.


몇 가지를 혼동하는 것 같습니다.

  1. 귀하의 예는 전화 operator new[]()가 아니라 operator new().

  2. 할당 함수 아무것도 생성하지 않습니다 . 그들은 할당 .

표현 T * p = new T[10]; 은 다음과 같은 원인이됩니다.

  1. operator new[]()size 인수 사용 하는 호출 10 * sizeof(T) + x,

  2. 의 기본 생성자에 대한 10 번의 호출을 T효과적으로 ::new (p + i) T().

유일한 특이점은 array-new 표현식 이 배열 데이터 자체에서 사용하는 것보다 더 많은 메모리를 요구 한다는 것입니다. 귀하는 이러한 정보를 볼 수 없으며, 묵묵히 수락하는 것 이외의 방법으로이 정보를 사용할 수 없습니다.


당신이 실제로 할당 된 메모리의 양 호기심이 있다면, 당신은 단순히 배열 할당 기능을 대체 할 수 operator new[]operator delete[]그것은 실제 크기를 인쇄합니다.


업데이트 : 임의의 정보로서, 전역 배치-새로운 기능작동 하지 않는 것이 필요 하다는 점에 유의해야합니다 . 즉, 객체 또는 배열을 다음과 같이 제자리에 생성 할 때 :

T * p = ::new (buf1) T;
T * arr = ::new (buf10) T[10];

그런 다음 호출에 대응 ::operator new(std::size_t, void*)하고 ::operator new[](std::size_t, void*)아무것도하지 않고 있지만, 자신의 두 번째 인수를 반환합니다. 그러나 당신은 무엇 buf10을 가리켜 야 하는지 모릅니다 : 그것은 10 * sizeof(T) + y메모리의 바이트 를 가리켜 야 하지만 당신은 알 수 없습니다 y.


어떤 버전의 호출도 operator new[] ()고정 된 크기의 메모리 영역에서 잘 작동하지 않습니다. 기본적으로 할당 된 메모리에 대한 포인터를 반환하는 대신 실제 메모리 할당 함수에 위임한다고 가정합니다. 객체 배열을 구성하려는 메모리 아레나가 이미있는 경우 객체를 사용 std::uninitialized_fill()하거나 std::uninitialized_copy()구성하기 (또는 객체를 개별적으로 구성하는 다른 형태) 를 원합니다 .

You might argue that this means that you have to destroy the objects in your memory arena manually as well. However, calling delete[] array on the pointer returned from the placement new won't work: it would use the non-placement version of operator delete[] ()! That is, when using placement new you need to manually destroy the object(s) and release the memory.


As mentioned by Kerrek SB in comments, this defect was first reported in 2004, and it was resolved in 2012 as:

The CWG agreed that EWG is the appropriate venue for dealing with this issue.

Then the defect was reported to EWG in 2013, but closed as NAD (presumably means "Not A Defect") with the comment:

The problem is in trying to use array new to put an array into pre-existing storage. We don't need to use array new for that; just construct them.

which presumably means that the suggested workaround is to use a loop with a call to non-array placement new once for each object being constructed.


A corollary not mentioned elsewhere on the thread is that this code causes undefined behaviour for all T:

T *ptr = new T[N];
::operator delete[](ptr);

Even if we comply with the lifetime rules (i.e. T either has trivial destruction, or the program does not depend on the destructor's side-effects), the problem is that ptr has been adjusted for this unspecified cookie, so it is the wrong value to pass to operator delete[].


After reading corresponding standard sections I am satarting to think that placement new for array types is simply useless idea, and the only reason for it being allowed by standard is generic way in which new-operator is described:

The new expression attempts to create an object of the typeid (8.1) or newtypeid to which it is applied. The type of that object is the allocated type. This type shall be a complete object type, but not an abstract class type or array thereof (1.8, 3.9, 10.4). [Note: because references are not objects, references cannot be created by newexpressions. ] [Note: the typeid may be a cvqualified type, in which case the object created by the newexpression has a cvqualified type. ]

new-expression: 
    ::(opt) new new-placement(opt) new-type-id new-initializer(opt)
    ::(opt) new new-placement(opt) ( type-id ) new-initializer(opt)

new-placement: ( expression-list )

newtypeid:
    type-specifier-seq new-declarator(opt)

new-declarator:
    ptr-operator new-declarator(opt)
    direct-new-declarator

direct-new-declarator:
    [ expression ]
    direct-new-declarator [ constant-expression ]

new-initializer: ( expression-list(opt) )

To me it seems that array placement new simply stems from compactness of the definition (all possible uses as one scheme), and it seems there is no good reason for it to be forbidden.

This leaves us in a situation where we have useless operator, which needs memory allocated before it is known how much of it will be needed. The only solutions I see would be to either overallocate memory and hope that compiler will not want more than supplied, or re-allocate memory in overriden array placement new function/method (which rather defeats the purpose of using array placement new in the first place).


To answer question pointed out by Kerrek SB: Your example:

void * addr = std::malloc(N * sizeof(T));
T * arr = ::new (addr) T[N];                // #1

is not always correct. In most implementations arr!=addr (and there are good reasons for it) so your code is not valid, and your buffer will be overrun.

About those "good reasons" - note that you are released by standard creators from some house-keeping when using array new operator, and array placement new is no different in this respect. Note that you do not need to inform delete[] about length of array, so this information must be kept in the array itself. Where? Exactly in this extra memory. Without it delete[]'ing would require keeping array length separate (as stl does using loops and non-placement new)


This overhead may be applied in all array new-expressions, including those referencing the library function operator new[](std::size_t, void*) and other placement allocation functions.

This is a defect in the standard. Rumor has it they couldn't find a volunteer to write an exception to it (Message #1165).

The non-replaceable array placement-new cannot be used with delete[] expressions, so you need to loop through the array and call each destructor.

The overhead is targetted at the user-defined array placement-new functions, which allocate memory just like the regular T* tp = new T[length]. Those are compatible with delete[], hence the overhead that carries the array length.

참고URL : https://stackoverflow.com/questions/8720425/array-placement-new-requires-unspecified-overhead-in-the-buffer

반응형