Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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++ 复制/移动构造函数能否安全地用于实现复制/移动赋值操作符?_C++_Design Patterns_C++11_Constructor_Idioms - Fatal编程技术网

C++ 复制/移动构造函数能否安全地用于实现复制/移动赋值操作符?

C++ 复制/移动构造函数能否安全地用于实现复制/移动赋值操作符?,c++,design-patterns,c++11,constructor,idioms,C++,Design Patterns,C++11,Constructor,Idioms,我认为下面的代码比复制和交换习惯用法要好 通过这种方式,您可以使用两个宏来封装复制和移动分配运算符的定义。换句话说,您可以避免在代码中显式定义它们。因此,您可以只将注意力集中在CTOR和dtor上 这种方法有什么缺点吗 class A { public: A() noexcept : _buf(new char[128]) {} ~A() noexcept { if (_buf) { del

我认为下面的代码比复制和交换习惯用法要好

通过这种方式,您可以使用两个宏来封装复制和移动分配运算符的定义。换句话说,您可以避免在代码中显式定义它们。因此,您可以只将注意力集中在CTOR和dtor上

这种方法有什么缺点吗

class A
{
public:
    A() noexcept
        : _buf(new char[128])
    {}

    ~A() noexcept
    {
        if (_buf)
        {
            delete[] _buf;
            _buf = nullptr;
        }
    }

    A(const A& other) noexcept
        : A()
    {
        for (int i = 0; i < 128; ++i)
        {
            _buf[i] = other._buf[i];
        }
    }

    A(A&& other) noexcept
        : _buf(other._buf)
    {
        _buf = nullptr;
    }

    A& operator =(const A& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(other);
        }

        return *this;
    }

    A& operator =(A&& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(static_cast<A&&>(other));
        }

        return *this;
    }

private:
    char* _buf;
};
A类
{
公众:
不例外
:_buf(新字符[128])
{}
~A()不例外
{
如果
{
删除[]_buf;
_buf=零PTR;
}
}
A(常数A和其他)无例外
:A()
{
对于(int i=0;i<128;++i)
{
_buf[i]=其他;
}
}
A(A&&其他)无例外
:_buf(其他._buf)
{
_buf=零PTR;
}
运算符=(常量A和其他)无异常(&O)
{
如果(此!=&其他)
{
这个->~A();
新(本)A(其他);
}
归还*这个;
}
A&运算符=(A&&其他)无例外
{
如果(此!=&其他)
{
这个->~A();
新(本)A(静态_铸造(其他));
}
归还*这个;
}
私人:
char*_buf;
};

它将在您提供的上下文中正常工作


当A是多态类且具有虚拟析构函数时,此技术将是灾难性的。

它将在您提供的上下文中正常工作

class A
{
public:
    A() noexcept
        : _buf(new char[128])
    {}
当A是多态类且具有虚拟析构函数时,这种技术将是灾难性的

class A
{
public:
    A() noexcept
        : _buf(new char[128])
    {}
在上面的例子中,
A()
将调用
std::terminate()
如果
newchar[128]
抛出异常

    ~A() noexcept
    {
        if (_buf)
        {
            delete[] _buf;
            _buf = nullptr;
        }
    }
在上面,看起来还可以。可以简化为:

    ~A() noexcept
    {
        delete[] _buf;
    }



    A(const A& other) noexcept
        : A()
    {
        for (int i = 0; i < 128; ++i)
        {
            _buf[i] = other._buf[i];
        }
    }
在上面,看起来不错

    A& operator =(const A& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(other);
        }

        return *this;
    }
在上面,通常我会说这是危险的。如果
new(这个)A(其他)怎么办抛出?在这种情况下,它不会,因为如果它尝试,程序将终止。这是否安全取决于应用程序(terminate对Ariane 5不起作用,但在更普通的应用程序中效果很好)

在上面的例子中,
A()
将调用
std::terminate()
如果
newchar[128]
抛出异常

    ~A() noexcept
    {
        if (_buf)
        {
            delete[] _buf;
            _buf = nullptr;
        }
    }
在上面,看起来还可以。可以简化为:

    ~A() noexcept
    {
        delete[] _buf;
    }



    A(const A& other) noexcept
        : A()
    {
        for (int i = 0; i < 128; ++i)
        {
            _buf[i] = other._buf[i];
        }
    }
在上面,看起来不错

    A& operator =(const A& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(other);
        }

        return *this;
    }
在上面,通常我会说这是危险的。如果
new(这个)A(其他)怎么办抛出?在这种情况下,它不会,因为如果它尝试,程序将终止。这是否安全取决于应用程序(terminate对Ariane 5不起作用,但在更普通的应用程序中效果很好)

通过为
\u buf
使用
unique\u ptr
,可以大大简化此类:

class A
{
public:
    static const std::size_t bufsize = 128;

    A() noexcept
        : _buf(new char[bufsize])
    {}

    A(const A& other) noexcept
        : A()
    {
        copy_from(other);
    }

    A(A&& other) noexcept = default;

    A& operator =(const A& other) noexcept
    {
        copy_from(other);
        return *this;
    }

    A& operator =(A&& other) noexcept = default;

private:
    void copy_from(const A& other) noexcept {
        std::copy_n(other._buf.get(), bufsize, _buf.get());
    }

    std::unique_ptr<char[]> _buf;
};
这里-所有隐式复制和移动。

您可以通过为
\u buf
使用
unique\u ptr
大大简化此类:

class A
{
public:
    static const std::size_t bufsize = 128;

    A() noexcept
        : _buf(new char[bufsize])
    {}

    A(const A& other) noexcept
        : A()
    {
        copy_from(other);
    }

    A(A&& other) noexcept = default;

    A& operator =(const A& other) noexcept
    {
        copy_from(other);
        return *this;
    }

    A& operator =(A&& other) noexcept = default;

private:
    void copy_from(const A& other) noexcept {
        std::copy_n(other._buf.get(), bufsize, _buf.get());
    }

    std::unique_ptr<char[]> _buf;
};

那里-所有隐式复制和移动。

你能给我举个例子吗?想象一下,A是一个“非最终”数据类,比如说,一个保持形状的笔刷和颜色。从A派生出的B类是一种特殊形状(例如圆)。然后代码
bb(…)
和后来的
b=A(…)
本打算刷涂
b
的颜色,但却调用了它的析构函数(因为析构函数是一个虚拟函数)。更糟糕的是,
new(this)A
行将B的虚拟表指针替换为A的虚拟表指针,这将导致很难找到bug。你能给我一个例子吗?想象一下,A是一个“非最终”数据类,比如说,一个保持形状的笔刷和颜色。从A派生出的B类是一种特殊形状(例如圆)。然后代码
bb(…)
和后来的
b=A(…)
本打算刷涂
b
的颜色,但却调用了它的析构函数(因为析构函数是一个虚拟函数)。更糟糕的是,行
new(this)A
将把B的虚拟表指针替换为A的虚拟表指针,这将导致难以发现的错误。默认构造函数
noexcept
的准确程度如何?你的移动构造器不会移动任何东西。在删除某些内容之前,无需检查
null ptr
。为什么
static\u cast
而不是
std::move
;只是移动操作。如果复制或移动构造函数可以引发异常,则您会遇到麻烦。如果您实际编写了
other,则可能是移动操作。\u buf=nullptr。正如Kerrek在上面所评论的,您的两个赋值运算符都不提供强大的异常保证,而copy&swap则提供。@Praetorian
noexcept
并不意味着“此函数中的任何内容都不能引发异常”。它意味着“此函数永远不会发出异常。”
noexcept
保证是函数接口的一个特征,与返回类型非常相似。这显然不是对如何实现该功能的约束。我在争论语义学,我知道你个人理解这一点。但是我觉得,对于那些认为
noexcept
是编译器针对
const
之类的实现进行验证的程序员来说,明确区分是很重要的。您的默认构造函数
noexcept
的准确程度如何?你的移动构造器不会移动任何东西。在删除某些内容之前,无需检查
null ptr
。为什么
static\u cast
而不是
std::move
;只是移动操作。如果复制或移动构造函数可以引发异常,则您会遇到麻烦。如果您实际编写了
other,则可能是移动操作。\u buf=nullptr。正如Kerrek在上面评论的那样,您的两个赋值运算符都不提供强有力的异常保证,而copy&s