名稱空間
變體
操作

C++ 命名需求: 分配器 (Allocator)

來自 cppreference.com
 
 
C++ 命名要求
基本
型別屬性
全庫範圍
分配器 (Allocator)




Container(容器)
容器元素
迭代器 (Iterator)
流 I/O
格式化器
隨機數
併發
範圍 (Ranges)
多維檢視
其他

 

封裝了物件的訪問/定址、分配/釋放和構造/析構的策略。

每一個可能需要分配或釋放儲存的標準庫元件,從 std::stringstd::vector 以及所有容器,除了 std::array(自 C++11 起)std::inplace_vector(自 C++26 起),到 std::shared_ptrstd::function(直到 C++17),都透過一個 分配器 (Allocator) 來完成:一個滿足以下要求的類型別物件。

許多分配器要求的實現是可選的,因為所有 分配器感知容器 (AllocatorAwareContainer) 都透過 std::allocator_traits 間接訪問分配器,而 std::allocator_traits 提供了這些要求的預設實現。

目錄

[編輯] 要求

給定

  • T,一個非 const、非引用型別(直到 C++11)非 const 物件型別(自 C++11 起)(直到 C++17) cv 不合格物件型別(自 C++17 起)
  • A,型別 T分配器 (Allocator) 型別,
  • a,型別 A 的物件,
  • B,對應於某個 cv 不合格物件型別 U分配器 (Allocator) 型別(透過重新繫結 A 獲得),
  • b,型別 B 的物件,
  • p,型別 std::allocator_traits<A>::pointer 的值,透過呼叫 std::allocator_traits<A>::allocate() 獲得,
  • cp,型別 std::allocator_traits<A>::const_pointer 的值,透過從 p 轉換獲得,
  • vp,型別 std::allocator_traits<A>::void_pointer 的值,透過從 p 轉換獲得,
  • cvp,型別 std::allocator_traits<A>::const_void_pointer 的值,透過從 cp 或從 vp 轉換獲得,
  • xp,指向某個 cv 不合格物件型別 X 的可解引用指標,
  • r,透過表示式 *p 獲得的型別 T 的左值,
  • n,型別 std::allocator_traits<A>::size_type 的值。
內部型別
型別 ID 別名型別 要求
A::pointer (可選) (未指定)[1]
A::const_pointer (可選) (未指定)
A::void_pointer (可選) (未指定)
  • 滿足 NullablePointer
  • A::pointer 可轉換為 A::void_pointer
  • B::void_pointerA::void_pointer 是相同的型別。
A::const_void_pointer (可選) (未指定)
  • 滿足 NullablePointer
  • A::pointerA::const_pointerA::void_pointer 可轉換為 A::const_void_pointer
  • B::const_void_pointerA::const_void_pointer 是相同的型別。
A::value_type T
A::size_type (可選) (未指定)
  • 無符號整數型別。
  • 可表示 A 能分配的最大物件的大小。
A::difference_type (可選) (未指定)
  • 有符號整數型別。
  • 可表示 A 分配的任意兩個物件指標之間的差值。
A::template rebind<U>::other
(可選)[2]
B
  • 對於任何 UB::template rebind<T>::otherA
指標操作
表示式 返回型別 要求
*p T&
*cp const T& *cp*p 標識同一個物件。
p->m (如同) (*p).m 相同,如果 (*p).m 有良好定義。
cp->m (如同) (*cp).m 相同,如果 (*cp).m 有良好定義。
static_cast<A::pointer>(vp) (如同) static_cast<A::pointer>(vp) == p
static_cast<A::const_pointer>(cvp) (如同) static_cast<A::const_pointer>(cvp) == cp
std::pointer_traits<A::pointer>::pointer_to(r) (如同)
儲存和生命週期操作
表示式 返回型別 要求
a.allocate(n) A::pointer 分配適合型別 T[n] 陣列物件的儲存並建立陣列,但不構造陣列元素。可能丟擲異常。如果 n == 0,則返回值未指定。
a.allocate(n, cvp) (可選) a.allocate(n) 相同,但可以以未指定的方式使用 cvpnullptr 或從 a.allocate() 獲得的指標)以幫助區域性性。
a.allocate_at_least(n) (可選) (自 C++23 起) std::allocation_result

    <A::pointer>

分配適合型別 T[cnt] 陣列物件的儲存並建立陣列,但不構造陣列元素,然後返回 {p, cnt},其中 p 指向儲存,cnt 不小於 n。可能丟擲異常。
a.deallocate(p, n) (未使用) 解除分配指向 p 的儲存,p 必須是先前呼叫 allocate allocate_at_least(自 C++23 起) 返回的值,並且沒有被中間的 deallocate 呼叫使無效。n 必須與先前傳遞給 allocate 的值匹配,或者在透過 allocate_at_least 請求和返回的元素數量之間(可以等於任一邊界)(自 C++23 起)。不丟擲異常。
a.max_size() (可選) A::size_type 可以傳遞給 A::allocate() 的最大值。
a.construct(xp, args...) (可選) (未使用) 在先前分配的儲存中,在 xp 指向的地址處,使用 args... 作為建構函式引數,構造一個型別為 X 的物件。
a.destroy(xp) (可選) (未使用) 析構 xp 指向的型別 X 的物件,但不解除分配任何儲存。
例項之間的關係
表示式 返回型別 要求
a1 == a2 bool
  • true 僅當分配器 a1 分配的儲存可以透過 a2 解除分配時。
  • 建立自反、對稱和傳遞關係。
  • 不丟擲異常。
a1 != a2
  • !(a1 == a2) 相同。
宣告 效果 要求
A a1(a) 複製構造 a1,使得 a1 == a
(注意:每個分配器 (Allocator) 也滿足 CopyConstructible。)
  • 不丟擲異常。
A a1 = a
A a(b) 構造 a,使得 B(a) == bA(b) == a
(注意:這意味著所有透過 rebind 關聯的分配器都維護彼此的資源,例如記憶體池。)
  • 不丟擲異常。
A a1(std::move(a)) 構造 a1,使其等於 a 的先前值。
  • 不丟擲異常。
  • a 的值未改變,且 a1 == a
A a1 = std::move(a)
A a(std::move(b)) 構造 a,使其等於 A(b) 的先前值。
  • 不丟擲異常。
型別 ID 別名型別 要求
A::is_always_equal
(可選)
std::true_typestd::false_type 或派生自此類。
對容器操作的影響
表示式 返回型別 描述
a.select_on_container_copy_construction()
(可選)
A
  • 提供一個 A 例項,供從當前使用 a 的容器複製構造的容器使用。
  • (通常返回 a 的副本或預設構造的 A。)
型別 ID 別名型別 描述
A::propagate_on_container_copy_assignment
(可選)
std::true_typestd::false_type 或派生自此類。
  • std::true_type 或派生自它,如果在使用它的容器被複制賦值時,需要複製型別 A 的分配器。
  • 如果此成員是 std::true_type 或派生自它,則 A 必須滿足 CopyAssignable,並且複製操作不能丟擲異常。
  • 請注意,如果源容器和目標容器的分配器不比較相等,複製賦值必須使用舊的分配器解除分配目標容器的記憶體,然後使用新的分配器分配記憶體,再複製元素(和分配器)。
A::propagate_on_container_move_assignment
(可選)
  • std::true_type 或派生自它,如果在使用它的容器被移動賦值時,需要移動型別 A 的分配器。
  • 如果此成員是 std::true_type 或派生自它,則 A 必須滿足 MoveAssignable,並且移動操作不能丟擲異常。
  • 如果未提供此成員或派生自 std::false_type,並且源容器和目標容器的分配器不比較相等,則移動賦值不能獲得源記憶體的所有權,必須單獨移動賦值或移動構造元素,並根據需要調整自身記憶體大小。
A::propagate_on_container_swap
(可選)
  • std::true_type 或派生自它,如果當兩個使用型別 A 分配器的容器交換時,需要交換這些分配器。
  • 如果此成員是 std::true_type 或派生自它,則型別 A 必須滿足 Swappable,並且交換操作不能丟擲異常。
  • 如果未提供此成員或派生自 std::false_type,並且兩個容器的分配器不比較相等,則容器交換的行為未定義。

注意

  1. 另請參閱下面的 花式指標
  2. rebind 僅在當此分配器是 SomeAllocator<T, Args> 形式的模板時才可選(由 std::allocator_traits 提供),其中 Args 是零個或多個額外的模板型別引數。

給定

  • x1x2,型別為 X::void_pointerX::const_void_pointerX::pointerX::const_pointer 的物件(可能不同)
那麼,x1x2等價值的指標值,當且僅當 x1x2 都可以透過僅使用這四種類型的 static_cast 序列顯式轉換為型別 X::const_pointer 的兩個相應物件 px1px2,並且表示式 px1 == px2 求值為 true

給定

  • w1w2,型別為 X::void_pointer 的物件
那麼,對於表示式 w1 == w2w1 != w2,一個或兩個物件都可以被型別為 X::const_void_pointer等價值物件替換,語義不變。

給定

  • p1p2,型別為 X::pointer 的物件
那麼,對於表示式 p1 == p2p1 != p2p1 < p2p1 <= p2p1 >= p2p1 > p2p1 - p2,一個或兩個物件都可以被型別為 X::const_pointer等價值物件替換,語義不變。

上述要求使得比較 容器 (Container)iteratorconst_iterator 成為可能。

分配器完整性要求

對於型別 T 的分配器型別 X 額外滿足分配器完整性要求,如果無論 T 是否是完整型別,以下兩項都為真

  • X 是一個完整型別。
  • 除了 value_typestd::allocator_traits<X> 的所有成員型別都是完整型別。
(C++17 起)

[編輯] 有狀態和無狀態分配器

每個分配器 (Allocator) 型別要麼是有狀態的,要麼是無狀態的。通常,有狀態分配器型別可以有不相等的值,表示不同的記憶體資源,而無狀態分配器型別表示單個記憶體資源。

儘管自定義分配器不要求是無狀態的,但在標準庫中使用有狀態分配器的方式和是否支援是實現定義的。如果實現不支援此類用法,使用不相等的分配器值可能導致實現定義的執行時錯誤或未定義行為。

(C++11 前)

自定義分配器可以包含狀態。每個容器或其他分配器感知物件都儲存一個提供的分配器例項,並透過 std::allocator_traits 控制分配器替換。

(C++11 起)

無狀態分配器型別的例項總是比較相等。無狀態分配器型別通常實現為空類,適用於 空基類最佳化

std::allocator_traits 的成員型別 is_always_equal 旨在用於確定分配器型別是否是無狀態的。

(C++11 起)

[編輯] 花式指標 (Fancy pointers)

當成員型別 pointer 不是原始指標型別時,它通常被稱為 “花式指標”。引入此類指標是為了支援分段記憶體架構,今天它們用於訪問與原始指標訪問的同構虛擬地址空間不同的地址空間中分配的物件。花式指標的一個示例是對映地址無關指標 boost::interprocess::offset_ptr,它使得在共享記憶體和記憶體對映檔案中分配基於節點的_資料結構(如 std::set)成為可能,這些檔案在每個程序中對映到不同的地址。花式指標可以獨立於提供它們的分配器使用,透過類模板 std::pointer_traits(自 C++11 起)函式 std::to_address 可用於從花式指標獲取原始指標。(自 C++20 起)

在標準庫中使用花式指標和自定義大小/不同型別是條件支援的。實現可能要求成員型別 pointerconst_pointersize_typedifference_type 分別是 value_type*const value_type*std::size_tstd::ptrdiff_t

(C++11 前)

概念

對於查詢物件 std::get_allocator 的定義,定義了以下僅用於闡釋的概念。

template<class Alloc>

concept /*simple-allocator*/ = requires(Alloc alloc, std::size_t n)
{
    { *alloc.allocate(n) } -> std::same_as<typename Alloc::value_type&>;
    { alloc.deallocate(alloc.allocate(n), n) };  
} && std::copy_constructible<Alloc>

  && std::equality_comparable<Alloc>;

僅用於闡釋的概念 /*simple-allocator*/ 定義了分配器 (Allocator) 需求的最小可用性約束。

(C++26 起)

[編輯] 標準庫

以下標準庫元件滿足分配器 (Allocator) 要求

預設分配器
(類模板) [編輯]
為多層容器實現多層分配器
(類模板) [編輯]
一個支援基於其構造的 std::pmr::memory_resource 的執行時多型性的分配器
(類模板) [編輯]

[編輯] 示例

演示一個 C++11 分配器,除了添加了 [[nodiscard]] 以匹配 C++20 樣式。

#include <cstdlib>
#include <iostream>
#include <limits>
#include <new>
#include <vector>
 
template<class T>
struct Mallocator
{
    typedef T value_type;
 
    Mallocator() = default;
 
    template<class U>
    constexpr Mallocator(const Mallocator <U>&) noexcept {}
 
    [[nodiscard]] T* allocate(std::size_t n)
    {
        if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
            throw std::bad_array_new_length();
 
        if (auto p = static_cast<T*>(std::malloc(n * sizeof(T))))
        {
            report(p, n);
            return p;
        }
 
        throw std::bad_alloc();
    }
 
    void deallocate(T* p, std::size_t n) noexcept
    {
        report(p, n, 0);
        std::free(p);
    }
private:
    void report(T* p, std::size_t n, bool alloc = true) const
    {
        std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n
                  << " bytes at " << std::hex << std::showbase
                  << reinterpret_cast<void*>(p) << std::dec << '\n';
    }
};
 
template<class T, class U>
bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; }
 
template<class T, class U>
bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; }
 
int main()
{
    std::vector<int, Mallocator<int>> v(8);
    v.push_back(42);
}

可能的輸出

Alloc: 32 bytes at 0x2020c20
Alloc: 64 bytes at 0x2023c60
Dealloc: 32 bytes at 0x2020c20
Dealloc: 64 bytes at 0x2023c60

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
LWG 179 C++98 pointerconst_pointer 不需要彼此可比較
不需要彼此可比較
需要
LWG 199 C++98 a.allocate(0) 的返回值不明確 未指定
LWG 258
(N2436)
C++98 分配器之間的相等關係不需要是自反、對稱或傳遞的
不需要是自反、對稱或傳遞的
要求是自反的,
對稱和傳遞的
LWG 274 C++98 T 可以是 const 限定型別或引用型別,
使得 std::allocator 可能格式錯誤[1]
禁止這些型別
LWG 2016 C++11 分配器的複製、移動和交換操作在使用時可能丟擲異常
分配器在使用時可能丟擲異常
要求不丟擲
LWG 2081 C++98
C++11
分配器不需要支援複製賦值 (C++98) 和移動賦值 (C++11)
分配器不需要支援複製賦值 (C++98) 和移動賦值 (C++11)
需要
LWG 2108 C++11 無法表示分配器是無狀態的 提供 is_always_equal
LWG 2263 C++11 LWG issue 179 的解決方案在 C++11 中意外丟失
並且未推廣到 void_pointerconst_void_pointer
恢復並推廣
LWG 2447 C++11 T 可以是 volatile 限定物件型別 禁止這些型別
LWG 2593 C++11 從分配器移動可能修改其值 禁止修改
P0593R6 C++98 allocate 不需要在其分配的儲存中建立陣列物件
不需要在其分配的儲存中建立陣列物件
需要
  1. std::allocator 的成員型別 referenceconst_reference 分別定義為 T&const T&
    • 如果 T 是引用型別,則 referenceconst_reference 格式錯誤,因為無法形成引用到引用(引用摺疊在 C++11 中引入)。
    • 如果 T 是 const 限定的,則 referenceconst_reference 相同,並且 address() 的過載集格式錯誤。
English 日本語 中文(简体) 中文(繁體)