2016-11-07 6 views
6

사용자 지정 할당 및 삭제로 일부 메모리 공간에서 작업하고 있습니다. malloc과 유사한 인터페이스 (예 : n 바이트 할당 또는 할당 된 ptr 해제)를 사용하여 발생합니다. 그래서 delete []와 같은 것은 없습니다.맞춤 할당 API를 사용하여 게재 위치 새로 고침을 어떻게 사용해야합니까?

이제 T의 배열을 만들고 싶습니다. 나는 그 공간을 auto space_ptr = magic_malloc(n*sizeof(T))으로 얻는다. 이제는 n-element를 in-place로 만들기 위해 array-placement-new와 같은 것을하고 싶습니다. 어떻게해야합니까? ... 아니면 1에서 n으로 루프를 돌리고 단일 T를 구성해야합니까?

: 여기 정렬 문제를 무시하고있어

  • (또는 오히려, alignof(T) 가정은 sizeof(T) 분할). 정렬을 처리하고 싶다면 더 좋을 수도 있지만 단순화를 위해 무시할 수도 있습니다.
  • C++ 11 코드 환영 (실제로는 선호), C++ 없음 14/17.
+0

배열에 새로운 배치 버전이 있지만 정렬 문제가있을 수 있습니다. [this] (http://coliru.stacked-crooked.com/a/01699890c4ae1ac0)가 필요합니까? – AndyG

+0

@AndyG : 배치'new []'는'delete []'에서 사용할 정보를 쓸 공간을 사용하기 때문에 아마 그렇지 않을 것입니다. – einpoklum

답변

5

귀하의 메모리가 귀하의 T에 대해 충분히 정렬되어 있다고 가정합니다. 아마 그걸 확인하고 싶을거야.

다음 문제는 예외입니다. 우리는 실제로 두 가지 버전을 작성해야합니다. 하나는 예외가 발생하고 다른 하나는없는 것입니다.

예외 안전 버전을 작성합니다.

template<class T, class...Args> 
T* construct_n_exception_safe(std::size_t n, void* here, Args&&...args) { 
    auto ptr = [here](std::size_t i)->void*{ 
    return static_cast<T*>(here)+i; 
    }; 
    for(std::size_t i = 0; i < n; ++i) { 
    try { 
     new(ptr(i)) T(args...); 
    } catch(...) { 
     try { 
     for (auto j = i; j > 0; --j) { 
      ptr(j-1)->~T(); 
     } 
     } catch (...) { 
     exit(-1); 
     } 
     throw; 
    } 
    } 
    return static_cast<T*>(here); 
} 

하고 있지 예외 안전 버전 :

template<class T, class...Args> 
T* construct_n_not_exception_safe(std::size_t n, void* here, Args&&...args) { 
    auto ptr = [here](std::size_t i)->void*{ 
    return static_cast<T*>(here)+i; 
    }; 
    for(std::size_t i = 0; i < n; ++i) { 
    new (ptr(i)) T(args...); 
    } 
    return static_cast<T*>(here); 
} 

당신은 Args&...에서 T을 구성하는 발생 여부를 경우에 따라 그들 사이에 선택할 수있는 태그 파견 기반 시스템을 할 수 있습니다. 던지면 ->~T()이 중요하지 않으므로 예외적 인 것을 사용하십시오.

C++ 17에서는 이러한 작업을 정확하게 수행하는 몇 가지 새로운 기능을 제공합니다. 그들은 아마도 내가하지 않은 코너 케이스를 처리 할 것입니다.


당신은 T 당신이 블록에서 만든 방법 T 많은 포함해야합니다 적지 않은 dtor이있는 경우, new[]delete[]을 모방하려는 경우.

일반적인 방법은 블록의 앞면에서 여분의 공간을 요청하는 것입니다. 즉, sizeof(T)*N+K을 요청하십시오. 여기서 Ksizeof(std::size_t) 일 수 있습니다.

에뮬레이터에서 을 첫 번째 비트에 넣은 다음 바로 뒤에 construct_n을 호출하십시오. delete[]에서

, 포인터 전달에서 sizeof(std::size_t) 빼기, N을 읽은 다음 개체를 파괴 (에서 오른쪽에서 왼쪽으로 시공 순서를 미러링).

이 모든 사항은주의해야합니다. try - catch.

~T()이 약간이라면 에뮬레이트 된 new[]과 은 모두 여분의 문자를 저장하지 않으며 읽지도 않습니다.

(이 new[]delete[]. 정확히 어떻게 new[]을 에뮬레이션하는 방법이며, delete[] 작업을 구현 의존합니다. 난 그냥 당신이 그들을 모방 수있는 하나의 방법을 스케치하고있어, 그들이 작동하는 방법과 호환되지 않을 수 있습니다 시스템에. 예를 들어, 일부 ABI를 항상 경우에도 ->~T() 사소한, 또는 다른 변화의 무수한 N을 저장할 수 있습니다.)


을 영업 이익으로 언급 한 바와 같이, 당신은 또한 사소한 건설을 확인 할 수 있습니다 이전에 위와 함께 성가시다.

+0

간단한 생성자를 사용하여 T에 최적화되어 있다고 확신 할 수 있습니까? 또한, 왜 던지기 (-1)를 던지면서 파괴합니까? – einpoklum

+0

@einpoklum 우리는 이미 그 시점에서 던지기를하고 있기 때문에. 건설은 던졌습니다. 소멸자가 던졌습니다. 우리는 기본적으로 망했다. 나는 그것을 최적화하는 데 자신이 없다. 물론 그 점을 확인해 보겠다 :하지만 먼저 위의 코드를 구현하고 불필요한 루핑을 일으키는 지 확인한다. 최소한 사소한 비트를 탐지하고 건너 뛰면 디버깅이 덜 우스꽝 스러울 것입니다. :) 당신은 태그 파견과'std :: is_trivial' 또는 somesuch를 사용할 것입니다. – Yakk

+1

할당의 시작 부분에'size_t '를 넣을 때주의하십시오. 할당 자체는 일반적으로 정렬되지만, 추가로'size_t '가 잘못 정렬 될 수 있습니다. 필자는 실제 애플리케이션에서 clang이 SIMD를 사용하여 클래스에서 두 개의 double을 초기화했으며, StdLib'new' /'malloc'에 의해 보장 된'alignof (max_align_t) '를 요구하는 반면,'sizeof (size_t) dyp