如何将自定义分配器的完全相同状态传递给多个容器?
How can I pass the exact same state of an custom allocator to multiple containers?
我正在编写一个分配器,它引用了一些 class 的另一个实例,它跟踪分配的字节数。
下面是我正在尝试做的事情的一个最小示例(改编自 here),只是没有整个内存跟踪 class,而是我引用了一些收集的 int到目前为止分配的字节数。此引用在 main 内部分配,应传递给 CustomAllocator:
#include <limits> // numeric_limits
#include <iostream>
#include <typeinfo> // typeid
// container
#include <vector>
#include <list>
#include <forward_list>
template<typename T>
class CustomAllocator {
public:
// type definitions
typedef T value_type; /** Element type */
typedef T* pointer; /** Pointer to element */
typedef T& reference; /** Reference to element */
typedef const T* const_pointer; /** Pointer to constant element */
typedef const T& const_reference; /** Reference to constant element */
typedef std::size_t size_type; /** Quantities of elements */
typedef std::ptrdiff_t difference_type; /** Difference between two pointers */
template<typename U>
struct rebind {
typedef CustomAllocator<U> other;
};
// return maximum number of elements that can be allocated
size_type max_size () const throw() {
return std::numeric_limits<std::size_t>::max() / sizeof(T);
}
CustomAllocator(std::size_t& memAllocated) :
m_totalMemAllocated(memAllocated) {
std::cout << "construct " << typeid(T).name() << std::endl;
}
CustomAllocator(const CustomAllocator& src) :
m_totalMemAllocated(src.m_totalMemAllocated) {
std::cout << "copy construct " << typeid(T).name() << std::endl;
}
template<class U>
CustomAllocator(const CustomAllocator<U>& src) :
m_totalMemAllocated(src.getTotalMemAllocated()) {
}
// allocate but don't initialize num elements of type T
pointer allocate(size_type num, const void* = 0) {
m_totalMemAllocated += num * sizeof(T);
// print message and allocate memory with global new
std::cout << "allocate " << num << " element(s)" << " of size "
<< sizeof(T) << std::endl;
pointer ret = (pointer) (::operator new(num * sizeof(T)));
std::cout << " allocated at: " << (void*) ret << std::endl;
return ret;
}
// deallocate storage p of deleted elements
void deallocate(pointer p, size_type num) {
m_totalMemAllocated -= num * sizeof(T);
// print message and deallocate memory with global delete
std::cout << "deallocate " << num << " element(s)" << " of size "
<< sizeof(T) << " at: " << (void*) p << std::endl;
::operator delete((void*) p);
}
// initialize elements of allocated storage p with value value
// no need to call rebind with this variadic template anymore in C++11
template<typename _U, typename ... _Args>
void construct(_U* p, _Args&&... args) {
::new ((void *) p) _U(std::forward<_Args>(args)...);
}
// destroy elements of initialized storage p
template<typename _U>
void destroy(_U* p) {
p->~_U();
}
// return address of values
pointer address (reference value) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
private:
std::size_t& m_totalMemAllocated;
};
template<typename T, typename U>
bool operator==(const CustomAllocator<T> a, const CustomAllocator<U>& b) {
return true;
}
template<typename T, typename U>
bool operator!=(const CustomAllocator<T>& a, const CustomAllocator<U>& b) {
return false;
}
int main() {
std::size_t memAllocated = 0;
CustomAllocator<int> allocatorInstance(memAllocated);
std::vector<int> foo(allocatorInstance);
foo.push_back(23);
foo.push_back(12);
foo.push_back(8);
std::cout << "---" << std::endl;
// here the same
std::list<double> bar(allocatorInstance);
bar.push_back(3.44);
bar.push_back(1.18);
bar.push_back(2.25);
std::cout << "---" << std::endl;
// debug output
for (auto x : foo)
std::cout << x << " ";
for (auto x : bar)
std::cout << x << " ";
std::cout << "\nalloc_count: " << memAllocated << std::endl;
std::cout << '\n';
return 0;
}
我的问题是我不知道如何将分配器实例的完全相同状态(在示例 m_totalMemAllocated 中)传递给其他两个容器(此处为:foo 和 bar)。因为标准说 C++11 分配器可以有一个状态。
更新:
感谢您到目前为止的回答:)
我知道您通常将 CustomAllocators 作为模板参数传递给 std 容器;像这样:
std::vector<int, CustomAllocator<int> > foo;
std::list<double, CustomAllocator<double> > bar;
另见:
但是我确实有一个我无法传递的状态,默认构造函数将被调用,除非我给引用一些默认值,否则我无法使用它(但这也不是我想要的).
将
std::size_t memAllocated = 0;
从 main 到全局范围意味着所有使用 CustomAllocator 的容器最终将使用全局定义的 memAllocated。但我想扩展它,这样我就可以拥有一些额外的内存或实例 memAllocated2,然后再次将其分配给其他一些分配器实例。
旁注:
对于与 STD 容器不同的容器的分配器的有状态版本,请参阅
How to track memory usage using EASTL?
为了确保状态在所有分配器实例之间共享,第一个想法是使其成为静态成员。但这还不够,因为不同的模板实例确实是不同的类型,每个实例都有自己的静态成员副本。所以我只能想象两种方法:使状态成为仅包含静态成员的辅助 class,或者使用单例模式:
辅助的静态成员class:
struct CustomAllocatorState {
static std::size_t& m_totalMemAllocated;
}
std::size_t& CustomAllocatorState::m_totalMemAllocated = 0; # do not forget definition...
template<typename T>
class CustomAllocator {
public:
...
pointer allocate(size_type num, const void* = 0) {
CustomAllocatorState::m_totalMemAllocated += num * sizeof(T);
...
单例模式(你可以使用任何其他 C++ 单例模式,这个非常简单但不能抵抗静态初始化失败):
class CustomAllocatorState {
CustomAllocatorState(): m_val(0) {}
static CustomAllocatorState state;
public:
int m_val;
static CustomAllocatorState& getState() {
return state;
}
};
CustomAllocatorState CustomAllocatorState::state;
template<typename T>
class CustomAllocator {
public:
...
CustomAllocator() :
state(CustomAllocatorState::getState()) {
std::cout << "construct " << typeid(T).name() << std::endl;
}
...
pointer allocate(size_type num, const void* = 0) {
state.m_totalMemAllocated += num * sizeof(T);
...
private:
CustomAllocatorState& state;
};
辅助中的静态成员 class 可能更简单,但如果您已经在应用程序中使用单例模式,那么在这里使用它也很有意义。
我正在编写一个分配器,它引用了一些 class 的另一个实例,它跟踪分配的字节数。
下面是我正在尝试做的事情的一个最小示例(改编自 here),只是没有整个内存跟踪 class,而是我引用了一些收集的 int到目前为止分配的字节数。此引用在 main 内部分配,应传递给 CustomAllocator:
#include <limits> // numeric_limits
#include <iostream>
#include <typeinfo> // typeid
// container
#include <vector>
#include <list>
#include <forward_list>
template<typename T>
class CustomAllocator {
public:
// type definitions
typedef T value_type; /** Element type */
typedef T* pointer; /** Pointer to element */
typedef T& reference; /** Reference to element */
typedef const T* const_pointer; /** Pointer to constant element */
typedef const T& const_reference; /** Reference to constant element */
typedef std::size_t size_type; /** Quantities of elements */
typedef std::ptrdiff_t difference_type; /** Difference between two pointers */
template<typename U>
struct rebind {
typedef CustomAllocator<U> other;
};
// return maximum number of elements that can be allocated
size_type max_size () const throw() {
return std::numeric_limits<std::size_t>::max() / sizeof(T);
}
CustomAllocator(std::size_t& memAllocated) :
m_totalMemAllocated(memAllocated) {
std::cout << "construct " << typeid(T).name() << std::endl;
}
CustomAllocator(const CustomAllocator& src) :
m_totalMemAllocated(src.m_totalMemAllocated) {
std::cout << "copy construct " << typeid(T).name() << std::endl;
}
template<class U>
CustomAllocator(const CustomAllocator<U>& src) :
m_totalMemAllocated(src.getTotalMemAllocated()) {
}
// allocate but don't initialize num elements of type T
pointer allocate(size_type num, const void* = 0) {
m_totalMemAllocated += num * sizeof(T);
// print message and allocate memory with global new
std::cout << "allocate " << num << " element(s)" << " of size "
<< sizeof(T) << std::endl;
pointer ret = (pointer) (::operator new(num * sizeof(T)));
std::cout << " allocated at: " << (void*) ret << std::endl;
return ret;
}
// deallocate storage p of deleted elements
void deallocate(pointer p, size_type num) {
m_totalMemAllocated -= num * sizeof(T);
// print message and deallocate memory with global delete
std::cout << "deallocate " << num << " element(s)" << " of size "
<< sizeof(T) << " at: " << (void*) p << std::endl;
::operator delete((void*) p);
}
// initialize elements of allocated storage p with value value
// no need to call rebind with this variadic template anymore in C++11
template<typename _U, typename ... _Args>
void construct(_U* p, _Args&&... args) {
::new ((void *) p) _U(std::forward<_Args>(args)...);
}
// destroy elements of initialized storage p
template<typename _U>
void destroy(_U* p) {
p->~_U();
}
// return address of values
pointer address (reference value) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
private:
std::size_t& m_totalMemAllocated;
};
template<typename T, typename U>
bool operator==(const CustomAllocator<T> a, const CustomAllocator<U>& b) {
return true;
}
template<typename T, typename U>
bool operator!=(const CustomAllocator<T>& a, const CustomAllocator<U>& b) {
return false;
}
int main() {
std::size_t memAllocated = 0;
CustomAllocator<int> allocatorInstance(memAllocated);
std::vector<int> foo(allocatorInstance);
foo.push_back(23);
foo.push_back(12);
foo.push_back(8);
std::cout << "---" << std::endl;
// here the same
std::list<double> bar(allocatorInstance);
bar.push_back(3.44);
bar.push_back(1.18);
bar.push_back(2.25);
std::cout << "---" << std::endl;
// debug output
for (auto x : foo)
std::cout << x << " ";
for (auto x : bar)
std::cout << x << " ";
std::cout << "\nalloc_count: " << memAllocated << std::endl;
std::cout << '\n';
return 0;
}
我的问题是我不知道如何将分配器实例的完全相同状态(在示例 m_totalMemAllocated 中)传递给其他两个容器(此处为:foo 和 bar)。因为标准说 C++11 分配器可以有一个状态。
更新:
感谢您到目前为止的回答:)
我知道您通常将 CustomAllocators 作为模板参数传递给 std 容器;像这样:
std::vector<int, CustomAllocator<int> > foo;
std::list<double, CustomAllocator<double> > bar;
另见:
但是我确实有一个我无法传递的状态,默认构造函数将被调用,除非我给引用一些默认值,否则我无法使用它(但这也不是我想要的).
将
std::size_t memAllocated = 0;
从 main 到全局范围意味着所有使用 CustomAllocator 的容器最终将使用全局定义的 memAllocated。但我想扩展它,这样我就可以拥有一些额外的内存或实例 memAllocated2,然后再次将其分配给其他一些分配器实例。
旁注:
对于与 STD 容器不同的容器的分配器的有状态版本,请参阅
How to track memory usage using EASTL?
为了确保状态在所有分配器实例之间共享,第一个想法是使其成为静态成员。但这还不够,因为不同的模板实例确实是不同的类型,每个实例都有自己的静态成员副本。所以我只能想象两种方法:使状态成为仅包含静态成员的辅助 class,或者使用单例模式:
辅助的静态成员class:
struct CustomAllocatorState {
static std::size_t& m_totalMemAllocated;
}
std::size_t& CustomAllocatorState::m_totalMemAllocated = 0; # do not forget definition...
template<typename T>
class CustomAllocator {
public:
...
pointer allocate(size_type num, const void* = 0) {
CustomAllocatorState::m_totalMemAllocated += num * sizeof(T);
...
单例模式(你可以使用任何其他 C++ 单例模式,这个非常简单但不能抵抗静态初始化失败):
class CustomAllocatorState {
CustomAllocatorState(): m_val(0) {}
static CustomAllocatorState state;
public:
int m_val;
static CustomAllocatorState& getState() {
return state;
}
};
CustomAllocatorState CustomAllocatorState::state;
template<typename T>
class CustomAllocator {
public:
...
CustomAllocator() :
state(CustomAllocatorState::getState()) {
std::cout << "construct " << typeid(T).name() << std::endl;
}
...
pointer allocate(size_type num, const void* = 0) {
state.m_totalMemAllocated += num * sizeof(T);
...
private:
CustomAllocatorState& state;
};
辅助中的静态成员 class 可能更简单,但如果您已经在应用程序中使用单例模式,那么在这里使用它也很有意义。