Интеллектуальный указатель C ++ 11 выполняет функции для объектов переменного размера

Представьте, что у меня есть класс без открытого конструктора, но со статическим методом фабрики или компоновщика, и размер созданного объекта зависит от аргументов, передаваемых фабрике (или компоновщику).

Есть ли способ создать общий (или уникальный) указатель на такой объект, используя std::make_shared (или же std::make_unique)?

Для любого предлагаемого полиморфизма я предпочитаю шаблоны виртуальным методам.

3

Решение

Считайте, что у меня есть класс без публичного конструктора …

Есть ли способ создать общий (или уникальный) указатель на такой объект, используя std::make_shared (или же std::make_unique)?

Там нет такого способа. std::make_shared а также std::make_unique требует публичного конструктора.

Вместо этого вы можете создать общий (уникальный) указатель без удобной функции make:

// within the factory
return std::shared_ptr<T>(new T(size));

PS. Стандартная библиотека C ++ 11 не имеет std::make_unique,

3

Другие решения

Если у вас есть фабричный метод, который создаст объект в блоке памяти, который вы ему предоставите, плюс метод, который сообщает вам, сколько места вам понадобится, вы можете сделать это.

Я собираюсь свободно использовать C ++ 14, добавить свой собственный typename а также ::type если вам действительно нужен C ++ 11.

Сначала мы предполагаем, что у нас есть это:

struct some_args {
// whatever
};
std::size_t how_big_is_X( some_args );
X* make_X( some_args, void* buffer );

с вышеизложенным, я могу сделать что-то функционально эквивалентное make_shared,

template<std::size_t Sz, std::size_t align=alignof(void*)>
struct smart_buffer_t {
void(*dtor)(void*) = 0;
std::aligned_storage_t< Sz, align> data;
template<class T, class...Args>
T* emplace(Args&&...args) {
return ctor( [&](void* pdata) {
::new( pdata ) T(std::forward<Args>(args)...);
} );
}
template<class F>
T* ctor( F&& f ) {
std::forward<F>(f)( (void*)&data );
dtor = [](void* ptr){
static_cast<T*>(ptr)->~T();
};
return static_cast<T*>((void*)&data);
}
~smart_buffer_t() {
if (dtor) dtor(&data);
}
};

template<class T, std::size_t Sz, std::size_t Algn=alignof(void*), class F>
std::shared_ptr<T>
make_sized( F&& f ) {
auto pbuff = std::make_shared<smart_buffer_t<Sz, Algn>>();
T* r = pbuff->ctor( std::forward<F>(f) );
return {r, pbuff}; // aliasing ctor
}

теперь у нас есть:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using pow_t = index_t< (1<<I) >;

std::shared_ptr<X>
make_shared_X( some_args args ) {
std::size_t Sz = how_big_is_X(args);
using pmaker = std::shared_ptr<X>(*)(some_args);
using maker_maker = [](auto Sz){
return +[](some_args args) {
return make_sized<X, Sz>([&](void* ptr){
return make_X(args, ptr);
});
};
};
static const pmaker table[] = {
maker_maker(pow_t<0>{}),
maker_maker(pow_t<1>{}),
maker_maker(pow_t<2>{}),
// ...
maker_maker(pow_t<63>{}), // assuming 64 bit size_t.
};
std::size_t i = 0;
while(Sz > (1<<i))
++i;
return table[i](args);
}

или что-то подобное.

Код не проверен. Он выделяет степень 2 в или больше, чем требуют ваши аргументы. Но объект создается в том же распределении, что и блок подсчета ссылок.

Можно использовать любой ряд вместо степеней 2, но размер таблицы должен быть достаточно большим, чтобы обрабатывать максимально возможное возвращаемое значение из how_big_is_X,

2

#include <memory>
#include <vector>
#include <iostream>

template <typename T>
std::unique_ptr<T> buildObjectWithSize(std::size_t size)  {
return std::unique_ptr<T>{T::buildObject(size)};
}

class MyObject {
public:
static MyObject* buildObject(std::size_t size) {
return new MyObject(size);
}

int& operator[](int i) {
return int_vector[i];
}
private:
MyObject(std::size_t size)
: int_vector(size) {
}

std::vector<int> int_vector;
};

int main () {
constexpr unsigned INT_VECTOR_SIZE{3U};

std::unique_ptr<MyObject> my_object_up{buildObjectWithSize<MyObject>(INT_VECTOR_SIZE)};

(*my_object_up.get())[1] = 5;
std::cout << (*my_object_up.get())[1] << '\n';

MyObject &my_object_ref = *my_object_up.get();
my_object_ref[2] = 3;
std::cout << my_object_ref[2] << '\n';
}
-1
По вопросам рекламы [email protected]