Tại sao tôi cần std :: get_tempional_buffer?


84

Tôi nên sử dụng cho mục đích std::get_temporary_buffernào? Tiêu chuẩn nói như sau:

Nhận một con trỏ tới bộ nhớ đủ để lưu trữ tối đa n đối tượng T liền kề.

Tôi đã nghĩ rằng bộ đệm sẽ được phân bổ trên ngăn xếp, nhưng điều đó không đúng. Theo Tiêu chuẩn C ++, bộ đệm này thực sự không phải là tạm thời. Hàm này có những ưu điểm gì so với hàm toàn cục ::operator new, hàm này cũng không tạo ra các đối tượng. Tôi có đúng rằng các câu sau đây là tương đương không?

int* x;
x = std::get_temporary_buffer<int>( 10 ).first;
x = static_cast<int*>( ::operator new( 10*sizeof(int) ) );

Chức năng này chỉ tồn tại cho đường cú pháp? Tại sao lại có temporarytên của nó?


Một trường hợp sử dụng đã được đề xuất trong Tạp chí Tiến sĩ Dobb, ngày 01 tháng 7 năm 1996 để triển khai các thuật toán:

Nếu không có bộ đệm nào có thể được cấp phát hoặc nếu nó nhỏ hơn yêu cầu, thuật toán vẫn hoạt động chính xác, Nó chỉ chậm lại.


2
FYI, std::get_temporary_buffersẽ không được chấp nhận trong C ++ 17.
Deqing

1
@Deqing Có. Nó cũng sẽ bị loại bỏ trong C ++ 20 và vì lý do chính đáng (như những thứ được đề cập bên dưới). Vì vậy, hãy di chuyển theo người xem ..
Nikos.

Câu trả lời:


44

Stroustrup nói trong "Ngôn ngữ lập trình C ++" ( §19.4.4 , SE):

Ý tưởng là một hệ thống có thể giữ một số bộ đệm có kích thước cố định sẵn sàng để phân bổ nhanh để yêu cầu không gian cho n đối tượng có thể mang lại không gian cho nhiều hơn n . Tuy nhiên, nó cũng có thể mang lại ít lợi nhuận hơn, vì vậy một cách sử dụng get_temporary_buffer()là lạc quan yêu cầu thật nhiều và sau đó sử dụng những gì có sẵn.
[...] Vì get_temporary_buffer()là cấp thấp và có khả năng được tối ưu hóa để quản lý bộ đệm tạm thời, nó không nên được sử dụng thay thế cho trình cấp phát mới hoặc phân bổ :: phân bổ () để có được bộ nhớ lâu hơn.

Anh ấy cũng bắt đầu giới thiệu về hai chức năng với:

Các thuật toán thường yêu cầu không gian tạm thời để thực hiện có thể chấp nhận được.

... nhưng dường như không cung cấp định nghĩa tạm thời hoặc dài hạn ở bất kỳ đâu.

Tuy nhiên, một giai thoại trong "Từ Toán học đến Lập trình Chung" đề cập rằng Stepanov đã cung cấp một triển khai trình giữ chỗ không có thật trong thiết kế STL ban đầu:

Trước sự ngạc nhiên của mình, nhiều năm sau, anh ấy phát hiện ra rằng tất cả các nhà cung cấp lớn cung cấp triển khai STL vẫn đang sử dụng cách triển khai khủng khiếp này […]


12
Có vẻ như việc thực hiện điều này của VC ++ chỉ đơn giản là một cuộc gọi vòng lặp operator newvới các đối số nhỏ hơn liên tiếp cho đến khi cấp phát thành công. Không có tối ưu hóa đặc biệt ở đó.
jalf

9
Tương tự với g ++ 4.5 - có vẻ như nó được dự định tốt nhưng bị các nhà cung cấp bỏ qua.
Georg Fritzsche

4
Có vẻ như họ nên gói chức năng này trong một lớp được gọi làcrazy_allocator
0xbadf00d

Nhưng chúng ta hãy nghiêm túc - Có thể một người sẽ rất vui khi phân bổ nhiều dung lượng. Những gì chúng ta có thể làm bây giờ là yêu cầu hệ thống có được lượng dung lượng lưu trữ khổng lồ bằng cách sử dụng get_temporary_buffer. Tuy nhiên, nếu chúng tôi nhận được ít hơn số tiền được yêu cầu (thật đáng tiếc), chúng tôi tiếp tục cố gắng thực hiện công việc của mình với bộ nhớ mà chúng tôi có. Có thể tốt hơn là bắt các bad_allocngoại lệ do cố gắng cấp phát nhiều bộ nhớ hơn khả năng. Tuy nhiên, mức độ hữu ích thực sự sẽ không thay đổi khi triển khai tốt.
0xbadf00d

@jalf Nó không được dùng nữa trong C ++ 17 :) (theo cppreference.com ).
4LegsDrivenCat

17

Anh chàng thư viện tiêu chuẩn của Microsoft nói như sau ( tại đây ):

  • Bạn có thể giải thích khi nào sử dụng 'get_tempional_buffer'

Nó có một mục đích rất chuyên biệt. Lưu ý rằng nó không ném ra các ngoại lệ, như new (nothrow), nhưng nó cũng không xây dựng các đối tượng, không giống như new (nothrow).

Nó được sử dụng nội bộ bởi STL trong các thuật toán như stable_partition (). Điều này xảy ra khi có các từ ma thuật như N3126 25.3.13 [alg.partitions] / 11: stable_partition () có độ phức tạp "Nhiều nhất (cuối cùng - đầu tiên) * hoán đổi log (cuối cùng - đầu tiên), nhưng chỉ hoán đổi số tuyến tính nếu có là đủ bộ nhớ bổ sung. " Khi dòng chữ ma thuật "nếu có đủ bộ nhớ bổ sung" xuất hiện, STL sử dụng get_tempional_buffer () để cố gắng giành được không gian làm việc. Nếu có thể, thì nó có thể triển khai thuật toán hiệu quả hơn. Nếu không thể, vì hệ thống đang chạy nguy hiểm gần hết bộ nhớ (hoặc phạm vi liên quan là rất lớn), thuật toán có thể rơi trở lại kỹ thuật chậm hơn.

99,9% người dùng STL sẽ không bao giờ cần biết về get_tempional_buffer ().


9

Tiêu chuẩn cho biết nó phân bổ bộ nhớ cho tối đa n các phần tử. Nói cách khác, ví dụ của bạn có thể trả về một bộ đệm đủ lớn chỉ cho 5 đối tượng.

Tuy nhiên, có vẻ như khá khó để hình dung một trường hợp sử dụng tốt cho việc này. Có lẽ nếu bạn đang làm việc trên một nền tảng hạn chế về bộ nhớ, thì đó là một cách thuận tiện để có được "càng nhiều bộ nhớ càng tốt".

Nhưng trên một nền tảng hạn chế như vậy, tôi sẽ tưởng tượng bạn sẽ bỏ qua trình cấp phát bộ nhớ càng nhiều càng tốt và sử dụng một nhóm bộ nhớ hoặc thứ gì đó mà bạn có toàn quyền kiểm soát.


4

Tôi nên sử dụng cho mục đích gì std::get_temporary_buffer?

Hàm không được chấp nhận trong C ++ 17, vì vậy câu trả lời đúng bây giờ là "không có mục đích, không sử dụng nó".


2
ptrdiff_t            request = 12
pair<int*,ptrdiff_t> p       = get_temporary_buffer<int>(request);
int*                 base    = p.first;
ptrdiff_t            respond = p.sencond;
assert( is_valid( base, base + respond ) );

phản hồi có thể ít hơn yêu cầu .

size_t require = 12;
int*   base    = static_cast<int*>( ::operator new( require*sizeof(int) ) );
assert( is_valid( base, base + require ) );

kích thước thực tế của cơ sở phải lớn hơn hoặc bằng yêu cầu .


2

Có lẽ (chỉ là phỏng đoán) nó có liên quan gì đó đến sự phân mảnh bộ nhớ. Nếu bạn tiếp tục phân bổ và phân bổ bộ nhớ tạm thời, nhưng mỗi lần bạn làm điều đó, bạn phân bổ một số bộ nhớ dự định dài hạn sau khi phân bổ tạm thời nhưng trước khi phân bổ nó, bạn có thể kết thúc với một đống phân mảnh (tôi đoán vậy).

Vì vậy, get_tempional_buffer có thể được dự định là một phần bộ nhớ lớn hơn mức bạn sẽ cần được cấp phát một lần (có lẽ có nhiều phần sẵn sàng để chấp nhận nhiều yêu cầu) và mỗi khi bạn cần bộ nhớ, bạn chỉ nhận được một trong các miếng, mảnh nhỏ. Vì vậy, bộ nhớ không bị phân mảnh.


1
Ý nghĩ rất thú vị. Mặc dù nó dường như hiện đang được thực hiện dưới dạng cho phép-chỉ-làm-điều gì đó-hoạt động bởi hầu hết các triển khai, điều này rất có thể được hỗ trợ chặt chẽ hơn và tích hợp với phần còn lại của các quy trình quản lý bộ nhớ. Tôi bỏ phiếu cho chúng tôi thực sự mong đợi điều này phù hợp với nó, và các bình luận của Bjarnes dường như cũng gợi ý điều đó.
gustaf r

Bây giờ tôi đã tìm kiếm những gì Bjarne nói về nó và anh ấy nói rằng nó được thiết kế để phân bổ nhanh mà không cần khởi tạo. Vì vậy, nó sẽ giống như toán tử void * chỉ dành cho bộ cấp phát (không phải bộ khởi tạo) mới (kích thước size_t), nhưng nhanh hơn để cấp phát, vì nó được cấp phát trước.
Daniel Munoz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.