Я пишу распределитель со ссылкой на другой экземпляр некоторого класса, который отслеживает количество выделенных байтов.
ниже приведен минимальный пример того, что я пытаюсь сделать (адаптировано из Вот), просто без целого класса отслеживания памяти, вместо этого я сделал ссылку на некоторый 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, см.
Чтобы убедиться, что состояние является общим для всех экземпляров распределителей, первой идеей было бы сделать его статическим членом. Но одного этого будет недостаточно, потому что разные экземпляры шаблона действительно являются разными типами, и у каждого будет своя копия статического члена. Поэтому я могу представить только 2 способа: сделать состояние вспомогательным классом, содержащим только статические члены, или использовать шаблон синглтона:
Статические члены вспомогательного класса:
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;
};
Статические члены во вспомогательном классе, вероятно, проще, но если вы уже используете одноэлементный шаблон в своем приложении, имеет смысл использовать его и здесь.
Других решений пока нет …