C++ 应使用独特的“ptr更容易实施”;“移动”;语义学?
编辑:使C++ 应使用独特的“ptr更容易实施”;“移动”;语义学?,c++,c++11,move-semantics,unique-ptr,C++,C++11,Move Semantics,Unique Ptr,编辑:使Foo和Bar变得不那么琐碎,而直接替换为shared\ptr更加困难 是否应将unique_ptr用作实现移动语义的更简单方法 像这样的班级 class Foo { int* m_pInts; bool usedNew; // other members ... public: Foo(size_t num, bool useNew=true) : usedNew(useNew) { if (usedNew)
Foo
和Bar
变得不那么琐碎,而直接替换为shared\ptr
更加困难
是否应将
unique_ptr
用作实现移动语义的更简单方法
像这样的班级
class Foo
{
int* m_pInts;
bool usedNew;
// other members ...
public:
Foo(size_t num, bool useNew=true) : usedNew(useNew) {
if (usedNew)
m_pInts = new int[num];
else
m_pInts = static_cast<int*>(calloc(num, sizeof(int)));
}
~Foo() {
if (usedNew)
delete[] m_pInts;
else
free(m_pInts);
}
// no copy, but move
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
Foo(Foo&& other) {
*this = std::move(other);
}
Foo& operator=(Foo&& other) {
m_pInts = other.m_pInts;
other.m_pInts = nullptr;
usedNew = other.usedNew;
return *this;
}
};
除了堆上始终存在
唯一\u ptr
实例的内存之外,这样的实现还存在哪些其他问题?这就是所谓的零规则
零规则规定大多数类不实现复制/移动分配/构造或销毁。而是将其委托给资源处理类
5的规则规定,如果您实现5个复制/移动分配/ctor或dtor中的任何一个,您应该实现或删除所有5个(或者,在适当考虑后,默认它们)
在您的例子中,mu pInts
应该是一个唯一的指针,而不是一个原始内存处理的缓冲区。如果它绑定到某个东西(比如大小),那么您应该编写一个指针和大小结构来实现5的规则。或者,如果可以接受3个指针而不是2个指针的开销,则只需使用std::vector
部分原因是您停止直接调用new
new
是直接管理资源的5种规则类型中的一个实现细节。业务逻辑类不会与new
混淆。它们既不新增,也不删除
unique\u ptr
只是资源管理类型的一个类别<代码> STD::String <代码>:STD::向量,代码> STD::SET//COD>,代码> SyddYPPTR < /C>,代码> STD::未来,<代码> STD::函数< /COD>——大多数C++ >代码> STD< /Cord>类型限定。写你自己的也是一个好主意。但是,当您这样做时,您应该将资源代码从“业务逻辑”中剥离出来
因此,如果您编写了一个std::function
clone,您可以使用unique\u ptr
或boost::value\u ptr
来存储函数对象的内部内脏。也许您甚至可以编写一个sbo\u value\u ptr
,它有时存在于堆上,有时存在于本地
然后用std::function
的“业务逻辑”来包装它,它理解所指向的东西是可调用的等等
“业务逻辑”
std::function
不会实现复制/移动分配/ctor,也不会实现析构函数。它可能会显式地=default
它们。是。您所寻找的是所谓的零规则(作为三/五规则的C++11扩展)。通过让您的数据都知道如何自我复制和移动,外部类不需要编写任何特殊的成员函数。编写这些特殊成员很容易出错,因此不必编写它们就可以解决很多问题
所以Foo
会变成:
class Foo
{
std::unique_ptr<size_t[]> data;
public:
Foo(size_t size): data(new size_t[size]) { }
};
class-Foo
{
std::唯一的ptr数据;
公众:
Foo(size\u t size):数据(新的size\u t[size]){}
};
这很容易证明这个结论的正确性 我的建议是分离关注点并使用组合
管理分配内存的生存期是智能指针的工作。如何将该内存(或其他资源)返回到运行时是智能指针的删除程序关心的问题
通常,如果您发现自己正在编写移动操作符和移动构造函数,那是因为您没有充分分解问题
例如:
#include <cstring>
#include <memory>
// a deleter
//
struct delete_or_free
{
void operator()(int* p) const
{
if (free_) {
std::free(p);
}
else {
delete [] p;
}
}
bool free_;
};
class Foo
{
//
// express our memory ownership in terms of a smart pointer.
//
using ptr_type = std::unique_ptr<int[], delete_or_free>;
ptr_type ptr_;
// other members ...
//
// some static helpers (reduces clutter in the constructor)
//
static auto generate_new(int size) {
return ptr_type { new int[size], delete_or_free { false } };
}
static auto generate_calloc(int size) {
return ptr_type {
static_cast<int*>(calloc(size, sizeof(int))),
delete_or_free { true }
};
}
public:
//
// our one and only constructor
//
Foo(size_t num, bool useNew=true)
: ptr_ { useNew ? generate_new(num) : generate_calloc(num) }
{
}
// it's good manners to provide a swap, but not necessary.
void swap(Foo& other) noexcept {
ptr_.swap(other.ptr_);
}
};
//
// test
//
int main()
{
auto a = Foo(100, true);
auto b = Foo(200, false);
auto c = std::move(a);
a = std::move(b);
b = std::move(c);
std::swap(a, b);
}
#包括
#包括
//删除者
//
结构删除\u或\u自由
{
void运算符()(int*p)常量
{
如果(免费){
std::游离(p);
}
否则{
删除[]p;
}
}
布尔自由度;
};
福班
{
//
//用智能指针表示我们的内存所有权。
//
使用ptr_type=std::unique_ptr;
ptr_型ptr_;
//其他成员。。。
//
//一些静态助手(减少构造函数中的混乱)
//
静态自动生成新(整数大小){
返回ptr_type{new int[size],delete_或_free{false};
}
静态自动生成\u calloc(整数大小){
返回ptr_类型{
静态_cast(calloc(size,sizeof(int)),
删除\u或\u free{true}
};
}
公众:
//
//我们唯一的构造函数
//
Foo(size\u t num,bool useNew=true)
:ptr{useNew?generate_new(num):generate_calloc(num)}
{
}
//提供交换是有礼貌的,但不是必须的。
无效掉期(Foo和其他)不例外{
ptr交换(其他ptr交换);
}
};
//
//试验
//
int main()
{
自动a=Foo(100,真);
自动b=Foo(200,假);
自动c=标准::移动(a);
a=标准::移动(b);
b=标准::移动(c);
标准:交换(a,b);
}
@Dan制作m_pInts
aunique\u ptr
不是很重要,除非它是一个公共成员,在你的代码库中使用了100次,其内容以摇摇欲坠的方式进出,有时它拥有自己的内容,有时它没有。但是如果是这样的话,你做什么都不容易,即使是你的Bar
计划也不容易。请在且仅在资源所有权唯一的情况下使用std::unique_ptr
。std::unique_ptr
上的移动语义仅用于转移所有权。史前的calloc
?@Dan,但这一点也不难…@Dan抱歉,我没能理解你的文字是这样的:你想使用一些移动语义,而std::unique\u ptr
仅用于此目的。@Dan我想你只要在允许编辑旧代码的地方插入智能指针就行了。我认为您知道,带有自定义删除器的智能指针可以处理任何类型的遗留资源的释放?e、 文件、套接字、卷曲句柄、sql句柄等?
#include <cstring>
#include <memory>
// a deleter
//
struct delete_or_free
{
void operator()(int* p) const
{
if (free_) {
std::free(p);
}
else {
delete [] p;
}
}
bool free_;
};
class Foo
{
//
// express our memory ownership in terms of a smart pointer.
//
using ptr_type = std::unique_ptr<int[], delete_or_free>;
ptr_type ptr_;
// other members ...
//
// some static helpers (reduces clutter in the constructor)
//
static auto generate_new(int size) {
return ptr_type { new int[size], delete_or_free { false } };
}
static auto generate_calloc(int size) {
return ptr_type {
static_cast<int*>(calloc(size, sizeof(int))),
delete_or_free { true }
};
}
public:
//
// our one and only constructor
//
Foo(size_t num, bool useNew=true)
: ptr_ { useNew ? generate_new(num) : generate_calloc(num) }
{
}
// it's good manners to provide a swap, but not necessary.
void swap(Foo& other) noexcept {
ptr_.swap(other.ptr_);
}
};
//
// test
//
int main()
{
auto a = Foo(100, true);
auto b = Foo(200, false);
auto c = std::move(a);
a = std::move(b);
b = std::move(c);
std::swap(a, b);
}