Câu hỏi này là về việc xây dựng các phiên bản của trình phân bổ tùy chỉnh trong khi chèn vào một bản đồ :: std.Bộ nhớ tùy chỉnh Allocator cho bản đồ STL
Đây là một cấp phát tùy chỉnh cho std::map<int,int>
cùng với một chương trình nhỏ mà sử dụng nó:
#include <stddef.h>
#include <stdio.h>
#include <map>
#include <typeinfo>
class MyPool {
public:
void * GetNext() {
return malloc(24);
}
void Free(void *ptr) {
free(ptr);
}
};
template<typename T>
class MyPoolAlloc {
public:
static MyPool *pMyPool;
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;
typedef T value_type;
template<typename X>
struct rebind
{ typedef MyPoolAlloc<X> other; };
MyPoolAlloc() throw() {
printf("-------Alloc--CONSTRUCTOR--------%08x %32s\n", this, typeid(T).name());
}
MyPoolAlloc(const MyPoolAlloc&) throw() {
printf(" Copy Constructor ---------------%08x %32s\n", this, typeid(T).name());
}
template<typename X>
MyPoolAlloc(const MyPoolAlloc<X>&) throw() {
printf(" Construct T Alloc from X Alloc--%08x %32s %32s\n", this, typeid(T).name(), typeid(X).name());
}
~MyPoolAlloc() throw() {
printf(" Destructor ---------------------%08x %32s\n", this, typeid(T).name());
};
pointer address(reference __x) const { return &__x; }
const_pointer address(const_reference __x) const { return &__x; }
pointer allocate(size_type __n, const void * hint = 0) {
if (__n != 1)
perror("MyPoolAlloc::allocate: __n is not 1.\n");
if (NULL == pMyPool) {
pMyPool = new MyPool();
printf("======>Creating a new pool object.\n");
}
return reinterpret_cast<T*>(pMyPool->GetNext());
}
//__p is not permitted to be a null pointer
void deallocate(pointer __p, size_type __n) {
pMyPool->Free(reinterpret_cast<void *>(__p));
}
size_type max_size() const throw() {
return size_t(-1)/sizeof(T);
}
void construct(pointer __p, const T& __val) {
printf("+++++++ %08x %s.\n", __p, typeid(T).name());
::new(__p) T(__val);
}
void destroy(pointer __p) {
printf("-+-+-+- %08x.\n", __p);
__p->~T();
}
};
template<typename T>
inline bool operator==(const MyPoolAlloc<T>&, const MyPoolAlloc<T>&) {
return true;
}
template<typename T>
inline bool operator!=(const MyPoolAlloc<T>&, const MyPoolAlloc<T>&) {
return false;
}
template<typename T>
MyPool* MyPoolAlloc<T>::pMyPool = NULL;
int main(int argc, char *argv[]) {
std::map<int, int, std::less<int>, MyPoolAlloc<std::pair<const int,int> > > m;
//random insertions in the map
m.insert(std::pair<int,int>(1,2));
m[5] = 7;
m[8] = 11;
printf("======>End of map insertions.\n");
return 0;
}
Đây là kết quả của chương trình này:
-------Alloc--CONSTRUCTOR--------bffcdaa6 St4pairIKiiE Construct T Alloc from X Alloc--bffcda77 St13_Rb_tree_nodeISt4pairIKiiEE St4pairIKiiE Copy Constructor ---------------bffcdad8 St13_Rb_tree_nodeISt4pairIKiiEE Destructor ---------------------bffcda77 St13_Rb_tree_nodeISt4pairIKiiEE Destructor ---------------------bffcdaa6 St4pairIKiiE ======>Creating a new pool object. Construct T Alloc from X Alloc--bffcd9df St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE +++++++ 0985d028 St4pairIKiiE. Destructor ---------------------bffcd9df St4pairIKiiE Construct T Alloc from X Alloc--bffcd95f St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE +++++++ 0985d048 St4pairIKiiE. Destructor ---------------------bffcd95f St4pairIKiiE Construct T Alloc from X Alloc--bffcd95f St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE +++++++ 0985d068 St4pairIKiiE. Destructor ---------------------bffcd95f St4pairIKiiE ======>End of map insertions. Construct T Alloc from X Alloc--bffcda23 St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE -+-+-+- 0985d068. Destructor ---------------------bffcda23 St4pairIKiiE Construct T Alloc from X Alloc--bffcda43 St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE -+-+-+- 0985d048. Destructor ---------------------bffcda43 St4pairIKiiE Construct T Alloc from X Alloc--bffcda43 St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE -+-+-+- 0985d028. Destructor ---------------------bffcda43 St4pairIKiiE Destructor ---------------------bffcdad8 St13_Rb_tree_nodeISt4pairIKiiEE
cuối hai cột của chương trình đầu ra một cấp phát cho std::pair<const int, int>
được xây dựng mỗi khi có một chèn vào bản đồ. Tại sao điều này lại cần thiết? Có cách nào để ngăn chặn điều này không?
Cảm ơn!
Chỉnh sửa: Mã này được thử nghiệm trên máy x86 với phiên bản g ++ 4.1.2. Nếu bạn muốn chạy nó trên máy 64 bit, bạn sẽ phải thay đổi ít nhất là dòng return malloc(24)
. Thay đổi thành return malloc(48)
sẽ hoạt động.
Rõ ràng không phải là bất hợp pháp khi xây dựng nó mỗi khi sản lượng trên cho thấy. Có một lý do cố hữu mà STL/C++ phải làm theo cách này? Nếu không, sau đó làm thế nào để bạn thay đổi việc thực hiện để ngăn chặn nó? –
Trong C++ 03, việc triển khai được cho phép giả định hơn một trình cấp phát là không trạng thái (trống). Bằng cách đó, việc tạo một bản sao gần như là miễn phí. Trong trường hợp của bạn, bạn có thể có thể có các nhà xây dựng bản sao và nhà điều hành chuyển nhượng sao chép con trỏ nội bộ, và không tạo ra một hồ bơi mới mỗi lần. –
@Bo - Chủ đề của câu hỏi không phải là người tạo bản sao! Thay vào đó, nó là hàm tạo tạo ra một trình cấp phát cho St4pairIKiiE từ một trình cấp phát cho St13_Rb_tree_nodeISt4pairIKiiEE. –