Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/140.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 应使用独特的“ptr更容易实施”;“移动”;语义学?_C++_C++11_Move Semantics_Unique Ptr - Fatal编程技术网

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
a
unique\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);
}