2016-11-07 14 views
6

Tôi đang làm việc trên một số không gian bộ nhớ với phân bổ và xóa tùy chỉnh, điều này xảy ra khi sử dụng giao diện giống như malloc mà tôi không kiểm soát (tức là phân bổ n byte hoặc miễn phí một ptr được phân bổ). Vì vậy, không có gì giống như xóa [].Tôi nên sử dụng vị trí mới với API phân bổ tùy chỉnh như thế nào?

Bây giờ, tôi muốn xây dựng một mảng của T. Tôi nhận được không gian cho điều đó với auto space_ptr = magic_malloc(n*sizeof(T)). Bây giờ tôi muốn làm một cái gì đó như mảng-vị trí mới để xây dựng các yếu tố n tại chỗ. Tôi nên làm như thế nào? ... hay tôi chỉ nên lặp từ 1 đến n và xây dựng T đơn?

Note:

  • Tôi phớt lờ vấn đề liên kết ở đây (hay đúng hơn, giả sử alignof(T) chia sizeof(T)). Nếu bạn muốn liên kết địa chỉ, điều đó thậm chí còn tốt hơn, nhưng bạn có thể bỏ qua nó để đơn giản.
  • Chào đón mã C++ 11 (ưa thích, trên thực tế), nhưng không có C++ 14/17.
+0

Có phiên bản vị trí mới cho mảng ... nhưng có thể có sự cố liên kết. Là [this] (http://coliru.stacked-crooked.com/a/01699890c4ae1ac0) những gì bạn cần? – AndyG

+0

@AndyG: Có thể không, bởi vì vị trí 'new []' sử dụng một số không gian để viết thông tin được sử dụng bởi 'delete []'. – einpoklum

Trả lời

5

Tôi cho rằng bộ nhớ của bạn được căn chỉnh đủ cho T. Bạn có thể muốn kiểm tra điều đó.

Vấn đề tiếp theo là ngoại lệ. Chúng ta thực sự nên viết hai phiên bản, một với khả năng xây dựng sẽ gây ra một ngoại lệ, và một không có.

Tôi sẽ viết phiên bản an toàn ngoại lệ.

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); 
} 

và không ngoại lệ phiên bản an toàn:

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); 
} 

Bạn có thể làm một hệ thống dựa trên thẻ-văn để chọn giữa chúng tùy thuộc vào nếu xây dựng T từ Args&... ném hay không. Nếu nó ném, và ->~T() là không tầm thường, hãy sử dụng một ngoại lệ an toàn.

C++ 17 cho thấy một số chức năng mới để thực hiện chính xác các tác vụ này. Họ có thể xử lý các trường hợp góc của tôi không.


Nếu bạn đang cố gắng bắt chước new[]delete[], nếu T có một dtor không tầm thường, bạn sẽ phải nhúng bao nhiêu T bạn đã tạo trong khối.

Cách điển hình để thực hiện việc này là yêu cầu thêm phòng để đếm số mặt trước của khối. Tức là, hãy yêu cầu sizeof(T)*N+K, trong đó K có thể là sizeof(std::size_t).

Bây giờ trong trình mô phỏng new[], hãy nhập N vào bit đầu tiên, sau đó gọi construct_n trên khối ngay sau đó.

Trong delete[], trừ sizeof(std::size_t) từ con trỏ được chuyển vào, đọc N và sau đó phá hủy các đối tượng (từ phải sang trái để xây dựng trình tự gương).

Tất cả điều này sẽ cần cẩn thận try - catch.

Nếu, tuy nhiên, ~T() là tầm thường, cả giả lập new[]delete[] không lưu trữ thêm std::size_t cũng như không đọc chúng.

(Lưu ý rằng đây là làm thế nào để thi đuanew[]delete[]. Làm thế nào chính xác new[]delete[] công việc là thực hiện phụ thuộc. Tôi chỉ phác thảo ra một cách để bạn có thể bắt chước họ, nó có thể không phù hợp với cách thức hoạt động trên hệ thống của bạn. Ví dụ, một số Abis có thể luôn luôn lưu trữ N ngay cả khi ->~T() là tầm thường, hoặc vô số các biến thể khác.)


theo ghi nhận của OP, bạn cũng có thể muốn kiểm tra để xây dựng tầm thường trước làm phiền với những điều trên.

+0

Chúng tôi tự tin rằng điều này sẽ được tối ưu hóa như thế nào cho T với một nhà xây dựng tầm thường? Ngoài ra, tại sao lối ra (-1) trên ném phá hủy? – einpoklum

+0

@einpoklum Bởi vì chúng tôi đã bàn giao một cú ném vào thời điểm đó. Xây dựng đã ném. Một kẻ hủy diệt đã ném. Về cơ bản chúng tôi hơi say. Tôi không tự tin rằng nó đang được tối ưu hóa; Tôi cũng sẽ kiểm tra điều đó: nhưng trước tiên tôi sẽ thực hiện ở trên, và xác nhận nó gây ra looping không cần thiết. Ít nhất, việc phát hiện và bỏ qua bit tầm thường đó sẽ làm cho việc gỡ lỗi ít lố bịch hơn. :) Bạn muốn sử dụng tính năng gửi thẻ và 'std :: is_trivial' hoặc somesuch. – Yakk

+1

Hãy cẩn thận khi đặt 'size_t' vào đầu phân bổ. Bản thân phân bổ thường được căn chỉnh, nhưng 'size_t' bổ sung có thể gây ra sự lệch hướng. Tôi đã chạy vào vấn đề này trong một ứng dụng thế giới thực, trong đó clang sử dụng SIMD để khởi tạo hai đôi trong một lớp, yêu cầu 'alignof (max_align_t)' đảm bảo bởi StdLib 'new' /' malloc', trong khi 'sizeof (size_t) dyp

Các vấn đề liên quan