C++11 智能指针 Make 可变大小对象函数
C++11 Smart Pointer Make Functions for Variable Sized Objects
假设我有一个 class 没有 public 构造函数,而是一个静态工厂或构建器方法,创建的对象的大小取决于传递给工厂(或构建器)的参数.
有没有办法使用 std::make_shared
(或 std::make_unique
)创建指向此类对象的共享(或唯一)指针?
对于任何建议的多态性,我更喜欢模板而不是虚拟方法。
Consider I have a class with no public constructor ...
Is there a way to create a shared (or unique) pointer to such an object using std::make_shared
(or std::make_unique
)?
没有这种方法。 std::make_shared
和 std::make_unique
需要一个 public 构造函数。
相反,您可以创建共享(唯一)指针而无需方便的“make”函数:
// within the factory
return std::shared_ptr<T>(new T(size));
PS。 C++11 标准库没有 std::make_unique
.
#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';
}
如果你有一个就地工厂方法可以在你提供的内存块中构造对象,再加上一个告诉你需要多少 space 的方法,你可以这样做.
我打算免费使用 C++14,如果你真的需要 C++11,请添加你自己的 typename
和 ::type
。
首先我们假设我们有这个:
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 的幂等于或大于您的 args 需求。但是对象是在与引用计数块相同的分配中构造的。
可以使用任何系列而不是 2 的幂,但是 table 大小必须足够大以处理来自 how_big_is_X
.[=17= 的最大可能 return 值]
假设我有一个 class 没有 public 构造函数,而是一个静态工厂或构建器方法,创建的对象的大小取决于传递给工厂(或构建器)的参数.
有没有办法使用 std::make_shared
(或 std::make_unique
)创建指向此类对象的共享(或唯一)指针?
对于任何建议的多态性,我更喜欢模板而不是虚拟方法。
Consider I have a class with no public constructor ...
Is there a way to create a shared (or unique) pointer to such an object using
std::make_shared
(orstd::make_unique
)?
没有这种方法。 std::make_shared
和 std::make_unique
需要一个 public 构造函数。
相反,您可以创建共享(唯一)指针而无需方便的“make”函数:
// within the factory
return std::shared_ptr<T>(new T(size));
PS。 C++11 标准库没有 std::make_unique
.
#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';
}
如果你有一个就地工厂方法可以在你提供的内存块中构造对象,再加上一个告诉你需要多少 space 的方法,你可以这样做.
我打算免费使用 C++14,如果你真的需要 C++11,请添加你自己的 typename
和 ::type
。
首先我们假设我们有这个:
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 的幂等于或大于您的 args 需求。但是对象是在与引用计数块相同的分配中构造的。
可以使用任何系列而不是 2 的幂,但是 table 大小必须足够大以处理来自 how_big_is_X
.[=17= 的最大可能 return 值]