C++标准库笔记四:用户定义分配器
下面的代码用gcc version 3.4.5 (mingw-vista special r3)测试。
注意,代码摘自《C++程序设计语言(特别版)》(略加修改),
里面的内存池和分配器实现都不是完整的。
// 编译命令行:
// g++ test008.cpp
//《C++程序设计语言(特别版)》
// 19.4.2 一个用户定义分配器
//演示标准库分配器allocator接口的简单实现
#include <cstddef>
#include <vector>
#include <iostream>
#include <iterator>
//class Pool定义一个简单接口的小内存块内存池
//这个内存池一次只能分配恒定大小的小内存块(由构造函数参数指定)
class Pool
{
private:
//用于分配和释放内存的链表
//比Chunk的粒度小,多个Link分割一个Chunk
//分配前Link的next域用于计算下一个分配位置
//分配后Link只是void*内存块的头部
struct Link
{
Link *next;
};
// 底层内存分配区的分区块
// 一个Pool内有多个依次链接的Chunk
//每个Chunk被多个Link分割成更小的内存块
//(但所有Link的排列次序不一定依照其所在Chunk块的排列次序),
//但因为内存是连续的,所以在增量分配和全体释放时速度会较快
struct Chunk
{
//让一个Chunk不超过8K且略小于8K
enum { SIZE = 8 * 1024 - 16 };
unsigned char mem[SIZE];
Chunk *next; //next应该不会超过16个字节
};
//多个Chunk前后组成一个链表(好像一个栈)
Chunk *chunks;
//每次alloc()返回的内存块最大大小,
//但不能比sizeof(Link)小,也不能比Chunk::SIZE大
const unsigned int esize;
//当前空闲Chunk块内的空闲Link头部
Link *head;
//禁用默认的复制构造函数和复制赋值,
//所以这里不需要定义方法体
private:
Pool(Pool& sz);
void operator=(Pool&);
private:
void grow();
public:
Pool(unsigned int n);//指定每次alloc()返回的内存块大小
~Pool();
void* alloc(); //不需要指定内存块大小(在构造函数中指定)
void free(void* b);
};
//从head链表的头部取出一个Link
//如果空闲Chunk块用完,需要分配新的Chunk块,
//然后用Link链表分割这个新的空闲Chunk块。
inline void* Pool::alloc()
{
if (head == 0)
{
grow();
}
Link *p = head;
head = p->next;
std::cout << "Pool::alloc(): p == " << p << ", head == " << head << std::endl;
return p;
}
//把要释放的b放回head链表的头部
inline void Pool::free(void* b)
{
Link* p = static_cast<Link*>(b);
p->next = head;
std::cout << "Pool::free(): p == " << p << ", head == " << head << std::endl;
head = p;
}
Pool::Pool(unsigned int sz)
: esize(sz < sizeof(Link) ? sizeof(Link) : sz)
{
head = 0;
chunks = 0;
}
Pool::~Pool()
{
Chunk *n = chunks;
while (n)
{
Chunk *p = n;
n = n->next;
delete p;
}
}
//增量分配内存
//虽然Link*所在的小内存块是连续的,
//但alloc的分配次序不一定是连续的
//(因为free()可能放回旧的内存块)
void Pool::grow()
{
Chunk *n = new Chunk();
n->next = chunks;
chunks = n;
std::cout << "Pool::grow(): esize == " << esize << std::endl;
const int nelem = Chunk::SIZE / esize;
unsigned char *start = n->mem;
unsigned char *last = &start[(nelem - 1) * esize];
for (unsigned char *p = start; p < last; p += esize)
{
reinterpret_cast<Link*>(p)->next = reinterpret_cast<Link*>(p + esize);
}
reinterpret_cast<Link*>(last)->next = 0; //见alloc()的if (head == 0)
head = reinterpret_cast<Link*>(start);
}
//template Pool_alloc把class Pool适配到C++标准库的分配器接口
//不完整,因为有些接口没有实现,而有些接口只实现部分功能
template<class T>
class Pool_alloc
{
private:
static Pool mem; //全局,以提高效率
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T *pointer;
typedef const T *const_pointer;
typedef T &reference;
typedef const T&const_reference;
Pool_alloc();
T* allocate(size_type n, void*);
void deallocate(pointer p, size_type n);
};
//构造静态的mem域
//因为Pool只能分配固定大小的小内存块,
//而且Pool_alloc没有实现rebind方法
//所以这里指定每次分配的连续内存区都必须是sizeof(T) * 128,
//虽然造成浪费,但确保分配的内存是连续的。
//没有超过Chunk块的8K上限,所以是安全的。
template <class T>
Pool Pool_alloc<T>::mem(sizeof(T) * 128);
template <class T>
Pool_alloc<T>::Pool_alloc()
{
}
template<class T>
T* Pool_alloc<T>::allocate(size_type n, void* = 0)
{
if (n <= 128)
{
void *p = mem.alloc();
std::cout << "allocate : " << n << ", " << p << std::endl;
return static_cast<T*>(p);
} else {
//TODO:
std::cout << "allocate : " << n << std::endl;
throw "allocate error"; //出错
}
}
template<class T>
void Pool_alloc<T>::deallocate(pointer p, size_type n)
{
if (n <= 128)
{
std::cout << "deallocate : " << n << ", " << p << std::endl;
mem.free(p);
return;
} else {
//TODO:
std::cout << "deallocate : " << n << std::endl;
throw "deallocate error"; //出错
}
}
int main(int argc, const char *argv[])
{
{
std::vector<int> V1;
V1.push_back(1);
V1.push_back(2);
V1.push_back(3);
V1.push_back(4);
std::cout << "V1:"<< std::endl;
std::copy(V1.begin(), V1.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
{
std::cout << "==================="<< std::endl;
try
{
std::vector< int, Pool_alloc<int> > V2;
std::cout << "V2.push_back(1);" << std::endl;
V2.push_back(1);
std::cout << "V2.push_back(2);" << std::endl;
V2.push_back(2);
std::cout << "V2.push_back(3);" << std::endl;
V2.push_back(3);
V2.push_back(4);
std::cout << "V2:"<< std::endl;
std::copy(V2.begin(), V2.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
catch (const char *&str)
{
std::cout << str << std::endl;
}
}
return 0;
}
/**
运行结果:
$ g++ -g test008.cpp
$ ./a.exe
V1:
1 2 3 4
===================
V2.push_back(1);
Pool::grow(): esize == 512
Pool::alloc(): p == 0x3e4d68, head == 0x3e4f68
allocate : 1, 0x3e4d68
V2.push_back(2);
Pool::alloc(): p == 0x3e4f68, head == 0x3e5168
allocate : 2, 0x3e4f68
deallocate : 1, 0x3e4d68
Pool::free(): p == 0x3e4d68, head == 0x3e5168
V2.push_back(3);
Pool::alloc(): p == 0x3e4d68, head == 0x3e5168
allocate : 4, 0x3e4d68
deallocate : 2, 0x3e4f68
Pool::free(): p == 0x3e4f68, head == 0x3e5168
V2:
1 2 3 4
deallocate : 4, 0x3e4d68
Pool::free(): p == 0x3e4d68, head == 0x3e4f68
*/
分享到:
相关推荐
- **C++与C标准库差异**:C++标准库命名格式去除了`.h`后缀,并加上前缀`c`,例如`<cstring>`替代`<cstring.h>`,同时所有名字空间化在`std`中。 - **`vector`类模板**:`vector`作为类模板,支持任意类型的数据存储...
C++的标准库提供了一套基于iostream的I/O系统,包括cin、cout、cerr、clog等,方便进行输入输出操作。 九、命名空间 命名空间是C++中为了避免全局命名冲突而引入的概念,它将相关的标识符组织在一起,减少了命名...
5. **STL(Standard Template Library)**:STL是C++的标准库,包含容器(如vector、list、set等)、迭代器、算法和函数对象,为程序员提供了高效的数据结构和算法。 6. **异常处理**:C++提供了异常处理机制,通过...
C++标准库中的`std::string`类提供了方便的字符串操作。 4. **结构体与联合体**:结构体是将不同数据类型组合在一起的数据结构,而联合体允许在相同的内存空间内存储不同类型的数据。 5. **类与对象**:C++的核心...
1. **string类**:C++标准库中的`std::string`类提供了一种方便的字符串操作。 2. **C++11的`std::array`类**:固定大小的数组容器,提供了类似STL容器的功能。 ### 学习建议 在学习C++的过程中,结合实际项目和...
7. **调试技巧**:调试是软件开发中的重要环节,笔记将介绍如何使用Visual C++的调试器进行断点设置、单步执行、查看变量值、调用堆栈分析等。 8. **其他高级主题**:可能还包括文件I/O操作、网络编程、数据库连接...
12. **STL(Standard Template Library)**:STL是C++标准库的一部分,包含了容器(如vector、list、map等)、算法和迭代器,极大地提高了编程效率。 学习C-C++的基础,不仅需要理解和掌握上述知识点,还需要通过...
8. 标准库应用:STL(Standard Template Library)是C++的标准库,包含容器(如vector、list)、算法和迭代器等,极大地提高了开发效率。C++(day10).txt可能介绍了STL的使用。 9. 多线程编程:C++11引入了内置的多...
6. **STL(Standard Template Library)**:STL是C++标准库的重要组成部分,包含容器、迭代器、算法和函数对象。了解并熟练使用STL能极大提升代码的效率和可读性。 7. **内存管理**:C++允许程序员直接管理内存,...
5. **STL(标准模板库)**:STL是C++的一个重要组成部分,包含容器(如vector、list、set)、迭代器、算法和函数对象,极大地简化了数据操作和算法实现。 6. **C++11及以后的新特性**:C++11引入了许多新特性,如...
同时,C++标准库还包含大量的实用函数,如字符串操作、时间处理等。 最后,良好的编程习惯和代码风格也是提高代码质量和团队协作效率的关键。遵循一定的命名规范,编写清晰的注释,合理使用代码组织结构(如命名...
- STL是C++的标准库,包含容器(如vector、list、map等)、算法和迭代器,是编写高效代码的必备工具。 8. **文件输入/输出**: - 文件操作是程序与外部世界交互的重要方式,学会如何读写文件,进行文本或二进制...
8. **流式I/O**:C++标准库提供iostream库,支持cin(输入)和cout(输出)进行文本流的处理,便于用户界面的开发。 9. **异常处理**:异常处理机制使程序能够优雅地处理错误情况,通过try-catch块捕获和处理运行时...
9. **标准库**:C++标准库提供了大量实用的功能,如输入/输出流(iostream)、容器(vector、list、map等)、算法(sort、find等)、字符串处理(string)等。熟悉并熟练使用这些库能提高编程效率。 10. **异常处理...
6. **STL(标准模板库)**:包括容器(如vector、list、map)、迭代器、算法等,这些预定义的工具极大地方便了程序员进行复杂的数据处理。 7. **异常处理**:学习如何在程序中捕获和处理错误,以提高程序的健壮性。...
- 标准模板库(STL):包括容器(如vector、list、set等)、算法和迭代器,极大地简化了编程工作。 - 并发编程:学习多线程和并发控制,提高程序效率。 - 高级特性:如Lambda表达式、右值引用、自动类型推断(auto)...
- **容器操作**:C++标准库提供了多种容器如`vector`, `list`, `map`等,用于存储和管理数据。 - **迭代器**:用于遍历容器中的元素,如`const_iterator`用于只读访问。 - **位集**:`bitset`是一个特殊的容器,用于...
16. 容器和算法:C++标准模板库(STL)提供了一系列容器和算法,用于处理数据集合。 17. 迭代器:迭代器是访问容器中的元素的一种方式,类似于指针。 这份笔记非常全面,覆盖了C++编程的方方面面,对于初学者来说,...
3. 标准模板库(STL):STL包括容器(如vector、list、map等)、迭代器、算法和函数对象,极大地丰富了C++的库功能。 4. 智能指针:智能指针如unique_ptr、shared_ptr和weak_ptr,用于自动管理动态分配的对象,防止...
6. **Boost库**:提供了许多C++标准库之外的实用工具,如Boost.Asio用于网络编程,Boost.Thread支持多线程等。 通过上述知识点的学习,初学者能够建立起扎实的C/C++编程基础,并逐渐掌握系统级编程和高级编程技巧,...