0
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;}
};

---------Driver CODE---------

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

---------Output With By Book Copy Constructor---------

emplace_back()

In Buffer()

push_back(Buffer(2))

In explicit Buffer(size_t const size)

In Buffer(Buffer && data)

obj Details : Length = 2: (ptr == nullptr) = false libc++abi.dylib: terminating with uncaught exception of type std::bad_alloc: std::bad_alloc

---------Output With Unnessary Fix in Copy Constructor---------

emplace_back()

In Buffer()

push_back(Buffer(2))

In explicit Buffer(size_t const size)

In Buffer(Buffer && data)

obj Details : Length = 2: (ptr == nullptr) = false

In Buffer(Buffer const& obj)

obj Details : Length = 0: (ptr == nullptr) = true Program ended with exit code: 0

---------Driver CODE---------

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

---------Output With By Book Copy Constructor---------

push_back(Buffer(2))

In explicit Buffer(size_t const size)

In Buffer(Buffer && data)

obj Details : Length = 2: (ptr == nullptr) = false

emplace_back()

In Buffer()

In Buffer(Buffer const& obj) Program ended with exit code: 0

---------Output With Unnessary Fix in Copy Constructor---------

push_back(Buffer(2))

In explicit Buffer(size_t const size)

In Buffer(Buffer && data)

obj Details : Length = 2: (ptr == nullptr) = false

emplace_back()

In Buffer()

In Buffer(Buffer const& obj)

obj Details : Length = 2: (ptr == nullptr) = false

Program ended with exit code: 0

1 Answers1

0

new unsigned char[obj.length] {0} exhibits undefined behavior when obj.length == 0.

[expr.new]/7 The expression in a noptr-new-declarator is erroneous if:
(7.4) — the new-initializer is a braced-init-list and the number of array elements for which initializers are provided ... exceeds the number of elements to initialize.

"Expression" here refers to the array size inside square brackets.

The copy constructor, which triggers this undefined behavior, is called by vector when it needs to reallocate its storage from 1 to 2 elements. That's why the problem manifests when you push zero-sized buffer first, then non-zero-sized (whereupon the zero-sized one is copied), but not the other way round.


You may want to mark your move constructor noexcept, then the vector would use it when reallocating, in place of the copy constructor.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85