欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中std::allocator的具體使用

 更新時間:2025年07月14日 09:54:40   作者:木心  
本文主要介紹了C++中的std::allocator的作用、內存分配與構造的解耦機制,以及placementnew的使用,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧

1.std::allocator

C++中的std::allocator默默工作在C++STL中的所有容器的內存分配上,很多內存池是按照std::allocator的標準來實現(xiàn)的,甚至很多開源的內存儲項目可以和大多數(shù)STL容器兼容,在很多場景下,內存池是std::allocator的優(yōu)化。

在C++中,傳統(tǒng)new操作符將內存分配(operator new,這里的operator new是C++的內存分配原語,默認調用C語言中的malloc,只是進行內存分配)和對象構造(構造函數(shù))耦合。即new運算符需要同時完成內存分配和對象構造兩個操作。

std::allocator將解耦內存分配和對象構造這兩個操作,按照C++11的標準,實現(xiàn)一個std::allocator需要包含以下的元素和方法:

  • value_type:將模板的參數(shù)類型T定義為value_type,如using value_type = T;或者typedef T value_type;
  • allocate():僅分配原始內存,功能就類似opeartor new
  • construct():在預分配的內存上構造對象(通過使用C++中的placement new機制)
  • destroy():析構對象但不釋放內存
  • deallocate():釋放原始內存(類似于operator delete

注釋: https://cplusplus.com/reference/memory/allocator/

1.1C++中的placement new 和operator new

placement new 是C++中一種特使的內存分配的對象構造機制,它允許在已分配的內存上直接構造對象,而不是通過傳統(tǒng)的new操作符同時分配內存和構造對象。

placement new的語法形式為:

new (pointer) Type(constructor_arguments);

其中:

  • pointer是指向已分配內存的指針
  • Type是要構造的對象
  • constructor_arguments是構造函數(shù)的參數(shù)

placement new的工作原理是,不調用operator new來分配內存,而是在給定的內存地址上直接調用構造函數(shù),最后返回傳入的指針(將指針類型轉換為目標類型)。placement new由C++標準庫提供默認實現(xiàn),不可重載:

// 標準庫中的 placement new 聲明(不可重載)
void* operator new(size_t, void* ptr) noexcept {
    return ptr;  // 直接返回傳入的指針
}

乍一看,這個placement new的實現(xiàn)什么都沒干,是如何完成對象的構造呢?其實是依靠語法來進行創(chuàng)建的:

new (pointer) Type(constructor_arguments);

這里仍然調用了Type(constructor_arguments),即調用了對象的構造函數(shù),在pointer指定的內存上進行構造,舉個例子:

#include <iostream>
struct Example {
    int value;
    Example(int val) : value(val) {
        std::cout << "Constructed at " << this << " with value " << value << std::endl;
    }
    ~Example() {
        std::cout << "Destructed at " << this << std::endl;
    }
};
int main() {
    // 手動分配一塊內存
    void* buffer = operator new(sizeof(Example));
    // 使用placement new在這塊內存上構造對象
    Example* obj = new (buffer) Example(42);
    // 顯式調用析構函數(shù)(這很重要?。?
    obj->~Example();
    // 釋放內存
    operator delete(buffer);
    return 0;
}

輸出為:

Constructed at 0x7fec4c400030 with value 42
Destructed at 0x7fec4c400030

operator new是C++的內存分配原語,默認調用malloc進行內存分配,返回void*,指向未初始化的原始內存,可以重載operator new以自定義其內存分配行為:

// 自定義全局 operator new
void* operator new(size_t size){
	std::cout << "Allocating " << size << " bytes\n";
	return malloc(size);
}

使用opeartor new和placement new的典型場景如下:

// 僅分配內存,不構造對象
void* raw_mem = operator new(sizeof(MyClass));

// 需要手動構造對象(例如通過 placement new)
MyClass* obj = new (raw_mem) MyClass();  // 調用構造函數(shù)

// 必須手動析構和釋放
obj->~MyClass();
operator delete(raw_mem);

核心區(qū)別

特性operator newplacement new
作用僅分配原始內存(不構造對象)在已分配的內存上構造對象
是否調用構造函數(shù)
內存來源通常來自于堆(可通過重載自定義)由程序員預先提供
語法void* p = operator new(size)new (ptr) Type(args...)
是否可重載可重載全局或類特定的operator new不能重載,已經(jīng)有固定實現(xiàn)

1.2一個custom allocator的實現(xiàn)

一個自定義的allocator需要實現(xiàn)以下的方法:

方法描述等效操作
allocate(n)分配n* sizeof(T)字節(jié)operator new
deallocate(p, n)釋放從p開始的n個元素operator delete
construct(p, args)在p構造對象(C++17已棄用)new(p) T(args...)
destroy(p)析構p處對象(C++17已棄用)p->~T()

注釋:C++17 后推薦通過 std::allocator_traits 訪問接口,以支持自定義分配器的可選方法。

按照C++11的標準實現(xiàn)一個allocator

#include <iostream>
#include <vector>
template<typename T>
class TrackingAllocator {
public:
    using value_type = T;

    TrackingAllocator() = default;
    
	// 支持 Rebinding(重新綁定)
    template<typename U>
    TrackingAllocator(const TrackingAllocator<U>&) {}

    T* allocate(size_t n) {
        size_t bytes = n * sizeof(T);
        std::cout << "Allocating " << bytes << " bytes\n";
        return static_cast<T*>(::operator new(bytes));
    }

    void deallocate(T* p, size_t n) {
        ::operator delete(p);
        std::cout << "Deallocating " << n * sizeof(T) << " bytes\n";
    }

    // 支持同類型分配器比較(無狀態(tài))
    bool operator==(const TrackingAllocator&) { return true; }
    bool operator!=(const TrackingAllocator&) { return false; }
};

// 使用示例
int main() {
    // 使用自定義分配器
    std::vector<int, TrackingAllocator<int>> vec;
    vec.push_back(42);  // 輸出分配信息
    vec.push_back(13);  // 輸出分配信息
    // 清空向量
    vec.clear();  // 輸出釋放信息
    return 0;
}

輸出:

Allocating 4 bytes
Allocating 8 bytes
Deallocating 4 bytes
Deallocating 8 bytes

1.3使用std::allocator_traits實現(xiàn)allocator

在 C++17 及之后版本中,推薦通過 std::allocator_traits 訪問分配器接口,而非直接調用分配器的方法。這是因為 allocator_traits 提供了一種統(tǒng)一且安全的方式來與分配器交互,即使自定義分配器沒有實現(xiàn)某些可選方法,也能通過默認實現(xiàn)正常工作。

  • 兼容性:即使自定義分配器未實現(xiàn)某些方法(如 construct/destroy),allocator_traits 會提供默認實現(xiàn)。
  • 靈活性:允許分配器僅實現(xiàn)必要的接口,其余由 allocator_traits 補充。
  • 標準化:所有標準庫容器(如 std::vector、std::list)內部都使用 allocator_traits 而非直接調用分配器。

注釋:https://cplusplus.com/reference/memory/allocator_traits/

關鍵接口對比(使用C++11標準 vs. C++17標準)

操作C++11,直接調用分配器allocC++17,通過allocator_traits(std::allocator_traits<Alloc>)
分配內存alloc.allocate(n)allocator_traits<Alloc>::allocate(alloc, n)
釋放內存alloc.deallocate(p, n)allocator_traits<Alloc>::deallocate(alloc, p, n)
構造對象alloc.construct(p, args)allocator_traits<Alloc>::construct(alloc, p, args...)
析構對象alloc.destroy(p)allocator_traits<Alloc>::destroy(alloc, p)
獲取最大大小alloc.max_size()allocator_traits<Alloc>::max_size(alloc)
重新綁定分配器類型alloc.rebind<U>::otherallocator_traits<Alloc>::rebind_alloc<U>

注釋:C++17 后 construct 和 destroy 被廢棄,推薦直接使用 std::allocator_traits 或 placement new/顯式析構。

舉個極簡分配器的例子:

#include <iostream>
#include <memory>  // std::allocator_traits

template <typename T>
struct SimpleAllocator {
    using value_type = T;

    // 必須提供 allocate 和 deallocate
    T* allocate(size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    void deallocate(T* p, size_t n) {
        ::operator delete(p);
    }
    // 不提供 construct/destroy,由 allocator_traits 提供默認實現(xiàn)
};

struct Widget {
    int id;
    Widget(int i) : id(i) { std::cout << "Construct Widget " << id << "\n"; }
    ~Widget() { std::cout << "Destroy Widget " << id << "\n"; }
};

int main() {
    using Alloc = SimpleAllocator<Widget>;
    Alloc alloc;
    // 1. 分配內存(通過 allocator_traits)
    auto p = std::allocator_traits<Alloc>::allocate(alloc, 1);
    // 2. 構造對象(即使 SimpleAllocator 沒有 construct 方法!)
    std::allocator_traits<Alloc>::construct(alloc, p, 42);  // 調用 Widget(42)
    // 3. 析構對象(即使 SimpleAllocator 沒有 destroy 方法?。?
    std::allocator_traits<Alloc>::destroy(alloc, p);
    // 4. 釋放內存
    std::allocator_traits<Alloc>::deallocate(alloc, p, 1);
    return 0;
}

輸出:

Construct Widget 42
Destroy Widget 42

一個更復雜的自定義分配器示例(帶狀態(tài))

#include <iostream>
#include <memory>  // std::allocator_traits

template <typename T>
class TrackingAllocator {
    size_t total_allocated = 0;
public:
    using value_type = T;

    T* allocate(size_t n) {
        total_allocated += n * sizeof(T);
        std::cout << "Allocated " << n * sizeof(T) << " bytes (Total: " << total_allocated << ")\n";
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* p, size_t n) {
        total_allocated -= n * sizeof(T);
        std::cout << "Deallocated " << n * sizeof(T) << " bytes (Remaining: " << total_allocated << ")\n";
        ::operator delete(p);
    }

    // 支持比較(相同類型的 TrackingAllocator 才等價)
    bool operator==(const TrackingAllocator& other) const {
        return false;  // 有狀態(tài),不同實例不能混用
    }
    bool operator!=(const TrackingAllocator& other) const {
        return true;
    }
};

int main() {
    using Alloc = TrackingAllocator<int>;
    Alloc alloc1, alloc2;

    auto p1 = std::allocator_traits<Alloc>::allocate(alloc1, 2);
    auto p2 = std::allocator_traits<Alloc>::allocate(alloc2, 3);
    // 必須用相同的 allocator 實例釋放!
    std::allocator_traits<Alloc>::deallocate(alloc1, p1, 2);
    std::allocator_traits<Alloc>::deallocate(alloc2, p2, 3);
    return 0;
}

輸出:

Allocated 8 bytes (Total: 8)
Allocated 12 bytes (Total: 12)
Deallocated 8 bytes (Remaining: 0)
Deallocated 12 bytes (Remaining: 0)

到此這篇關于C++中std::allocator的具體使用的文章就介紹到這了,更多相關C++ std::allocator內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關文章

最新評論