首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

The rule of three/five/zero

第三条规则

如果类需要用户定义的破坏者,用户定义的复制构造函数,或者用户定义的复制赋值算子几乎可以肯定地说,这需要这三个方面。

由于C++在各种情况下复制和分配用户定义类型的对象%28传递/返回值、操作容器等%29,这些特殊的成员函数将被调用,如果它们是可访问的,并且如果它们不是用户定义的,则它们是由编译器隐式定义的。

如果类的句柄为非类类型的对象(%28原始指针、POSIX文件描述符等)(其析构函数不执行任何操作),而复制构造函数/赋值操作符执行“浅拷贝”%28复制句柄的值,则隐式定义的特殊成员函数通常是不正确的。而不重复基础资源%29。

二次

代码语言:javascript
复制
class rule_of_three
{
    char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block
 public:
    rule_of_three(const char* arg)
    : cstring(new char[std::strlen(arg)+1]) // allocate
    {
        std::strcpy(cstring, arg); // populate
    }
    ~rule_of_three()
    {
        delete[] cstring;  // deallocate
    }
    rule_of_three(const rule_of_three& other) // copy constructor
    {
        cstring = new char[std::strlen(other.cstring) + 1];
        std::strcpy(cstring, other.cstring);
    }
    rule_of_three& operator=(const rule_of_three& other) // copy assignment
    {
        char* tmp_cstring = new char[std::strlen(other.cstring) + 1];
        std::strcpy(tmp_cstring, other.cstring);
        delete[] cstring;
        cstring = tmp_cstring;
        return *this;
    }
// alternatively, reuse destructor and copy ctor
//  rule_of_three& operator=(rule_of_three other)
//  {
//      std::swap(cstring, other.cstring);
//      return *this;
//  }
};

二次

通过可复制句柄管理不可复制资源的类可能必须声明副本分配和复制构造函数私有,而不提供它们的定义或将它们定义为已删除。这是第三条规则的另一种应用:删除一个,而另一个则由隐式定义,很可能会导致错误。

五条规则

因为用户定义的析构函数、复制构造函数或复制赋值操作符的存在阻止了对移动构造函数而移动赋值算子,任何需要移动语义的类都必须声明所有五个特殊成员函数:

二次

代码语言:javascript
复制
class rule_of_five
{
    char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block
 public:
    rule_of_five(const char* arg)
    : cstring(new char[std::strlen(arg)+1]) // allocate
    {
        std::strcpy(cstring, arg); // populate
    }
    ~rule_of_five()
    {
        delete[] cstring;  // deallocate
    }
    rule_of_five(const rule_of_five& other) // copy constructor
    {
        cstring = new char[std::strlen(other.cstring) + 1];
        std::strcpy(cstring, other.cstring);
    }
    rule_of_five(rule_of_five&& other) : cstring(other.cstring) // move constructor
    {
        other.cstring = nullptr;
    }
    rule_of_five& operator=(const rule_of_five& other) // copy assignment
    {
        char* tmp_cstring = new char[std::strlen(other.cstring) + 1];
        std::strcpy(tmp_cstring, other.cstring);
        delete[] cstring;
        cstring = tmp_cstring;
        return *this;
    }
    rule_of_five& operator=(rule_of_five&& other) // move assignment
    {
        if(this!=&other) // prevent self-move
        {
            delete[] cstring;
            cstring = other.cstring;
            other.cstring = nullptr;
        }
        return *this;
    }
// alternatively, replace both assignment operators with 
//  rule_of_five& operator=(rule_of_five other)
//  {
//      std::swap(cstring, other.cstring);
//      return *this;
//  }
};

二次

与规则三不同,未能提供移动构造函数和移动分配通常不是错误,而是错过了优化机会。

零规则

具有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符的类应专门处理所有权%28,这是由单一责任原则29%。其他类不应具有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符。[1]

二次

代码语言:javascript
复制
class rule_of_zero
{
    std::string cppstring;
 public:
    rule_of_zero(const std::string& arg) : cppstring(arg) {}
};

二次

当基类用于多态使用时,它的析构函数可能必须声明为公共和虚拟的。这将阻止隐式移动%28,并放弃隐式副本%29,因此必须将特殊成员函数声明为默认成员函数。[2]

二次

代码语言:javascript
复制
class base_of_five_defaults
{
 public:
    base_of_five_defaults(const base_of_five_defaults&) = default;
    base_of_five_defaults(base_of_five_defaults&&) = default;
    base_of_five_defaults& operator=(const base_of_five_defaults&) = default;
    base_of_five_defaults& operator=(base_of_five_defaults&&) = default;
    virtual ~base_of_five_defaults() = default;
};

二次

但是,如果派生类的对象未被动态分配,或者动态分配仅存储在std::shared_ptr28%,如std::make_shared%29:即使在转换到std::shared_ptr<Base>...

参考文献

代码语言:txt
复制
 ? cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com