Добрый день!
Задача: создавать очень быстро std::shared_ptr для объекта небольшого класса.
Подскажите пожалуйста как правильно работать с std::shared_ptr && allocator/deallocator чтобы избежать двойного выделения памяти для самого объекта и control block.
#include <iostream>
#include <memory>
#include "memory_allocator.hpp"
class Frame
{
public:
Frame() {std::cout << __func__ << std::endl;};
~Frame() {std::cout << __func__ << std::endl;};
};
//----------------------------------------------------------------------
int main()
{
MemoryAllocator<Frame> allocator;
auto deleter = [&](Frame* frame) {
std::cout << "[deleter called] " << frame << std::endl;
allocator.deallocate(frame);
};
auto ptr = allocator.allocate();
std::shared_ptr<Frame> frame(ptr, deleter, allocator);
return 0;
}
Простой аллокатор, запиленный по примерам в интернете:
template<typename T>
class MemoryAllocator
{
public:
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
MemoryAllocator() noexcept {std::cout << "ctor " << this << std::endl;};
MemoryAllocator(const MemoryAllocator&) noexcept {std::cout << "copy ctor " << this << std::endl;};
template<typename U>
MemoryAllocator(const MemoryAllocator<U>&) noexcept {std::cout << "template copy ctor " << this << std::endl;};
~MemoryAllocator() noexcept {std::cout << "dtor " << this << std::endl;};
pointer allocate() {
auto ptr = static_cast<pointer>(::operator new(sizeof(T)));
std::cout << this << " " <<__func__ << " " << ptr << std::endl;
return ptr;
}
//----------------------------------------------------------------------
pointer allocate(size_type n, const void* = 0) {
auto ptr = static_cast<pointer>(::operator new(n * sizeof(T)));
std::cout << this << " " << __func__ << " " << ptr << " " << n << std::endl;
return ptr;
}
//----------------------------------------------------------------------
void deallocate(pointer ptr) {
std::cout << this << " " << __func__ << " " << ptr << std::endl;
}
//----------------------------------------------------------------------
void deallocate(pointer ptr, size_type) {
std::cout << this << " " << __func__ << " " << ptr << std::endl;
}
};
Получаем такой вывод.
ctor 0x7fff54e392d8
0x7fff54e392d8 allocate 0x1074010
copy ctor 0x7fff54e39298
copy ctor 0x7fff54e39238
copy ctor 0x7fff54e391d8
template copy ctor 0x7fff54e39170
0x7fff54e39170 allocate 0x1074030 1
copy ctor 0x1074040
dtor 0x7fff54e39170
dtor 0x7fff54e391d8
dtor 0x7fff54e39238
dtor 0x7fff54e39298
[deleter called] 0x1074010
0x7fff54e392d8 deallocate 0x1074010
template copy ctor 0x7fff54e391c0
dtor 0x1074040
0x7fff54e391c0 deallocate 0x1074030
dtor 0x7fff54e391c0
dtor 0x7fff54e392d8
Как видно, вроде все хорошо, сам объект и control блок выделяются через аллокатор и удаляются так же.
Но!
Почему здесь там много всяких copy ctor ?
Как можно использовать только один аллокатор и почему он так много раз копируется ?
P.S. извините за скудные познания в memory allocation, первый раз сталкиваюсь. Спасибо!
Здравствуйте, lnkuser, Вы писали:
L>Подскажите пожалуйста как правильно работать с std::shared_ptr && allocator/deallocator чтобы избежать двойного выделения памяти для самого объекта и control block.
std::allocate_shared
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, lnkuser, Вы писали:
L>>Подскажите пожалуйста как правильно работать с std::shared_ptr && allocator/deallocator чтобы избежать двойного выделения памяти для самого объекта и control block.
W>W>std::allocate_shared
W>
при std::allocate_shared не получится указать deleter и вывод примерно такой же, тоже много ctor && dtor...
Здравствуйте, lnkuser, Вы писали:
L>Здравствуйте, watchmaker, Вы писали:
W>>Здравствуйте, lnkuser, Вы писали:
L>>>Подскажите пожалуйста как правильно работать с std::shared_ptr && allocator/deallocator чтобы избежать двойного выделения памяти для самого объекта и control block.
W>>W>>std::allocate_shared
W>>
L>при std::allocate_shared не получится указать deleter
Конечно не получится! В этом весь смысл. Хоть какая-то защита от выстрела в ногу. Или как по-твоему deleter может ужиться с вышепроцитированным условием про shared_ptr и аллокацию/деаллокацию памяти одновременно под объект и под control block? Это же взаимоисключающие стратегии поведения.
Или нужно не выделения памяти под объект и control block единым участком своим аллокатором? А вопрос в чём-то другом?
L> и вывод примерно такой же, тоже много ctor && dtor...
Ну и что? Аллокаторы должны копироваться — они и копируются. Как они иначе должны своё состояние сохранять?
А если состояния у аллокатора нет — так вообще проблема отсутствует: никого не волнует, например, миллиарды копирований
std::allocator, если каждое из них стоит ровно 0 времени и 0 памяти.