std::allocator和 __pool_alloc
在 C++ 中,std::allocator
和 __pool_alloc
是两种不同的内存分配器,它们在内存管理策略和性能上有显著的区别。
std::allocator
- 标准分配器:
std::allocator
是 C++ 标准库提供的默认分配器,它简单地封装了::operator new
和::operator delete
,用于动态分配和释放内存。 - 适用场景:适用于大多数标准容器,如
std::vector
、std::list
等。它简单且通用,但在处理大量小对象时可能效率较低。
__pool_alloc
- 内存池分配器:
__pool_alloc
是 GNU C++ 标准库(libstdc++)提供的一个扩展分配器,它使用内存池技术来管理内存。内存池分配器预先分配一大块内存,并将其分割成固定大小的小块,通过链表管理这些小块。 - 性能优势:在处理大量小对象时,
__pool_alloc
的性能通常优于std::allocator
,因为它减少了对::operator new
的调用次数,从而减少了内存分配和释放的开销。 - 线程安全:
__pool_alloc
提供了一个布尔模板参数thr
,用于控制是否启用线程安全。当thr=true
时,分配器是线程安全的,但性能会略低;当thr=false
时,分配器在单线程环境下性能更高。 - 适用场景:适用于需要频繁分配和释放大量小对象的场景,如
std::list
等容器。
使用示例
以下是一个使用 __pool_alloc
的示例代码:
#include <iostream>
#include <vector>
#include <ext/pool_allocator.h>
int main() {
int nLoop = 0;
std::cin >> nLoop;
std::vector<int, __gnu_cxx::__pool_alloc<int>> vec;
for (int i = 0; i < nLoop; i++) {
vec.push_back(i);
}
std::cout << "alloc end\n";
std::string str;
std::cin >> str;
return 0;
}
总结
std::allocator
:简单通用,适合大多数场景,但在处理大量小对象时可能效率较低。__pool_alloc
:使用内存池技术,适合处理大量小对象,提供线程安全选项,性能在某些场景下优于std::allocator
。
选择哪种分配器取决于具体的应用场景和性能需求。如果需要优化小对象的分配性能,__pool_alloc
是一个不错的选择。
在 C++ STL 容器中,分配器对象(如 allocator_
)通常不是静态的。它是一个普通的成员变量,每个容器实例都有自己的分配器对象。这样设计的原因是为了支持容器的灵活性和可定制性,同时允许不同的容器实例使用不同的分配器策略。
在 C++ STL 中,容器(如 std::vector
、std::list
等)通过模板参数接受一个分配器(Allocator),用于管理内存分配和释放。默认情况下,容器使用 std::allocator
,但用户可以提供自定义分配器来满足特定的内存管理需求。
容器如何使用分配器
1. 模板参数
容器模板通常有两个参数:
- 第一个参数是容器存储的元素类型。
- 第二个参数是分配器类型,默认为
std::allocator
。
例如:
template <typename T, typename Allocator = std::allocator<T>>
class vector;
2. 分配器的接口
分配器需要实现以下接口:
allocate(size_type n)
:分配足够存储n
个元素的内存。deallocate(pointer p, size_type n)
:释放之前分配的内存。construct(pointer p, Args&&... args)
:在分配的内存上构造对象。destroy(pointer p)
:销毁对象。
3. 容器内部的使用
容器在内部通过分配器来管理内存。以下是一个简化的 std::vector
的实现,展示了如何使用分配器:
#include <iostream>
#include <memory>
#include <cstddef>
template <typename T, typename Allocator = std::allocator<T>>
class Vector {
public:
typedef T value_type;
typedef Allocator allocator_type;
typedef typename allocator_type::pointer pointer;
typedef typename allocator_type::size_type size_type;
private:
pointer data_;
size_type size_;
size_type capacity_;
allocator_type allocator_;
public:
Vector() : data_(nullptr), size_(0), capacity_(0), allocator_() {}
explicit Vector(size_type n, const T& value = T(), const Allocator& alloc = Allocator())
: size_(n), capacity_(n), allocator_(alloc) {
data_ = allocator_.allocate(n);
for (size_type i = 0; i < n; ++i) {
allocator_.construct(data_ + i, value);
}
}
~Vector() {
for (size_type i = 0; i < size_; ++i) {
allocator_.destroy(data_ + i);
}
allocator_.deallocate(data_, capacity_);
}
// 其他成员函数...
};
int main() {
Vector<int> vec(5, 10); // 创建一个包含5个元素的vector,每个元素初始化为10
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
自定义分配器的使用
用户可以提供自定义分配器来满足特定的内存管理需求。以下是一个简单的自定义分配器示例:
#include <cstddef>
#include <new>
#include <stdexcept>
template <typename T>
class MyAllocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
pointer allocate(size_type n) {
void* p = ::operator new(n * sizeof(T));
if (!p) throw std::bad_alloc();
return static_cast<pointer>(p);
}
void deallocate(pointer p, size_type n) {
::operator delete(p);
}
template <typename... Args>
void construct(pointer p, Args&&... args) {
::new(p) T(std::forward<Args>(args)...);
}
void destroy(pointer p) {
p->~T();
}
};
template <typename T, typename U>
bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) {
return true;
}
template <typename T, typename U>
bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) {
return false;
}
使用自定义分配器的容器:
#include <vector>
int main() {
std::vector<int, MyAllocator<int>> vec(5, 10);
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
总结
容器通过模板参数接受分配器,并在内部使用分配器的接口来管理内存。默认情况下,容器使用 std::allocator
,但用户可以提供自定义分配器来优化内存管理。自定义分配器需要实现 allocate
、deallocate
、construct
和 destroy
等方法。