首页 > 解决方案 > 为什么行为会随着 emplace_back / push_back 顺序的变化而变化?为什么构造函数的数量增加了?

问题描述

class Buffer
{
    unsigned char* ptr{nullptr};
    size_t length{0};
public:
    Buffer() :
    ptr(nullptr),
    length(0)
    {std::cout<<"\n In Buffer()\n";}


    explicit Buffer(size_t const size):
    ptr(new unsigned char[size] {0}),
    length (size)
    {std::cout<<"\n In explicit Buffer(size_t const size)\n";}

    ~Buffer()
    {
        if (nullptr != ptr)     delete [] ptr;
    }


//     Causing runtime nullptr exception
//     Buffer(Buffer const& obj) :
//     ptr(new unsigned char[obj.length] {0}),
//     length (obj.getLength())
//     {   std::cout<<"\n In Buffer(Buffer const& obj)\n";}

    // Unnessary Fixed Copy Constructor
    Buffer(Buffer const& obj) noexcept
    {
        std::cout<<"\n In Buffer(Buffer const& obj)\n";
        std::cout<<"\n obj Details : Length = "<<obj.getLength()<<std::boolalpha<<": (ptr == nullptr) = "<<(nullptr == obj.getPtr())<<"\n";
        if (obj.length == 0)
            ptr = nullptr;
        else
            ptr = new unsigned char[obj.length] {0};
        length = obj.length;
    }

    Buffer& operator=(Buffer const obj)
    {
        std::cout<<"\n In Buffer& operator=(Buffer const obj)\n";
        if (this == &obj)   return *this;
        delete [] this->ptr;
        this->ptr = new unsigned char[obj.length]{0};
        this->length = obj.length;
        return *this;
    }

    Buffer(Buffer && data)
    {
        std::cout<<"\n In Buffer(Buffer && data)\n";
        std::cout<<"\n obj Details : Length = "<<data.getLength()<<std::boolalpha<<": (ptr == nullptr) = "<<(nullptr == data.getPtr())<<"\n";
        ptr = data.ptr;
        length = data.length;
        data.ptr = nullptr;
        data.length = 0;
    }

    Buffer& operator=(Buffer&& data)
    {
        std::cout<<"\n In Buffer& operator=(Buffer&& data)\n";
        if (this == &data)  return *this;
        this->ptr = data.ptr;
        this->length = data.length;
        data.ptr = nullptr;
        data.length = 0;
        return *this;
    }

    size_t getLength() const {return length;}
    unsigned char* getPtr() const {return ptr;}
};

---------驱动程序代码------

std::vector<Buffer> v;
std::cout<<"\nemplace_back()\n";
v.emplace_back();
std::cout<<"\npush_back(Buffer(2))\n";
v.push_back(Buffer(2));

--------- 使用 By Book Copy 构造函数输出---------

emplace_back()

在缓冲区()

push_back(缓冲区(2))

在显式缓冲区中(size_t const size)

在缓冲区(缓冲区 && 数据)

obj 详细信息:长度 = 2: (ptr == nullptr) = false libc++abi.dylib:以未捕获的 std::bad_alloc 类型异常终止:std::bad_alloc

---------复制构造函数中带有不必要修复的输出---------

emplace_back()

在缓冲区()

push_back(缓冲区(2))

在显式缓冲区中(size_t const size)

在缓冲区(缓冲区 && 数据)

obj 详细信息:长度 = 2: (ptr == nullptr) = false

在缓冲区中(缓冲区常量& obj)

obj详细信息:长度= 0:(ptr == nullptr)= true程序以退出代码结束:0

---------驱动程序代码------

std::vector<Buffer> v;
std::cout<<"\npush_back(Buffer(2))\n";
v.push_back(Buffer(2));
std::cout<<"\nemplace_back()\n";
v.emplace_back();

--------- 使用 By Book Copy 构造函数输出---------

push_back(缓冲区(2))

在显式缓冲区中(size_t const size)

在缓冲区(缓冲区 && 数据)

obj 详细信息:长度 = 2: (ptr == nullptr) = false

emplace_back()

在缓冲区()

In Buffer(Buffer const& obj) 程序以退出代码结束:0

---------复制构造函数中带有不必要修复的输出---------

push_back(缓冲区(2))

在显式缓冲区中(size_t const size)

在缓冲区(缓冲区 && 数据)

obj 详细信息:长度 = 2: (ptr == nullptr) = false

emplace_back()

在缓冲区()

在缓冲区中(缓冲区常量& obj)

obj 详细信息:长度 = 2: (ptr == nullptr) = false

程序以退出代码结束:0

标签: c++11move-semanticspush-back

解决方案


new unsigned char[obj.length] {0}时表现出未定义的行为obj.length == 0

[expr.new]/7 noptr-new-declarator中的表达式在以下情况下是错误的: (7.4) — new-initializer是一个花括号初始化列表,并且为其提供初始化器的数组元素的数量...超过要初始化的元素数量。

这里的“表达式”是指方括号内的数组大小。

触发此未定义行为的复制构造函数vector在需要将其存储从 1 个元素重新分配到 2 个元素时被调用。这就是为什么当您首先推送零大小的缓冲区,然后是非零大小的缓冲区(因此复制零大小的缓冲区)时出现问题,但反之则不然。


您可能想要标记您的移动构造函数noexcept,然后向量将在重新分配时使用它来代替复制构造函数。


推荐阅读