c++memory-managementqueue

Is there a way to stop a memory leak with a queue and a custom struct with a character array?


I am creating a custom object Message, and then appending it to a queue. This will call the copy constructor; however, the addresses after a memmove() don't seem to match like I would expect them to.

The code is as follows

#include <iostream>
#include <queue>

struct Message
{
  char* data = nullptr;
  size_t size = 0;

  Message() = default;

  ~Message()
  {
    delete[] data;
  }

  Message(const Message& other)
  {
      this->data = new char[other.size];
      this->size = other.size;

      memmove(this->data,
              other.data,
              this->size);
  }
};

std::queue<Message> message_queue;

int main()
{
    Message message;

    message.size = 50;
    message.data = new char[message.size];

    std::cout << &message.data << std::endl;

    message_queue.push(std::move(message));

    Message other_message = message_queue.front();
    message_queue.pop();

    std::cout << &other_message.data << std::endl;

    return 0;
}

I am wondering why pushing this item onto the queue and grabbing it's memory address is different. Should the addresses not be the same? And is this a memory leak if I don't define the ~Message() to delete[] data;

edit: This most certainly is a memory leak if i don't delete on destructor. But why is the memory not in the same location?

edit 2:

I now have the class as follows:

#include <iostream>
#include <queue>
#include <cstring>
#include <utility>

struct Message
{
    char* data = nullptr;
    size_t size = 0;

    Message(size_t in_size)
    {
        size = in_size;
        data = new char[size];
    }

    ~Message()
    {
        delete[] data;
    }

    Message(const Message& other)
    {
        std::cout << "Message(const Message& other)\n";

        this->size = other.size;
        this->data = new char[this->size];

        memmove(this->data,
                other.data,
                this->size);
    }

    Message& operator=(const Message& other)
    {
        std::cout << "Message& operator=(const Message& other)\n";

        if (this == &other)
        {
            return *this;
        }

        Message temp(other);

        std::swap(size, temp.size);
        std::swap(data, temp.data);

        return *this;
    }

    Message(Message&& other) noexcept
        : data(std::exchange(other.data, nullptr))
    {
        std::cout << "Message(Message&& other) noexcept\n";
    }

    Message& operator=(Message&& other) noexcept
    {
        std::cout << "Message& operator=(Message&& other) noexcept\n";

        Message temp(std::move(other));

        std::swap(data, temp.data);
        std::swap(size, temp.size);

        return *this;
    }
};

std::queue<Message> message_queue;

int main()
{
    Message message(50);

    message_queue.push(message);

    Message other_message = message_queue.front();
    message_queue.pop();

    std::cout << (void*) message.data << "\n" << (void*) other_message.data << "\n";

    return 0;
}

And my output still is showing that data is pointing to two different locations.

Message(const Message& other)
Message(const Message& other)
0x1b0c2a53400
0x1b0c2a52f40

I believe I am still doing something wrong I am just unsure


Solution

  • message_queue.push(std::move(message));

    This uses the copy constructor you've defined (since you didn't provide a move constructor). Right there, you will allocate new memory and copy the data into that. So, other.data and this->data will point at different addresses.

    Message other_message = message_queue.front();

    This uses the copy constructor a second time, again allocating new memory and copying the data into that. The new object's data will point at a third address.

    You now have three different memory allocations, all being leaked if you don't delete[] the memory in ~Message().

    Is there a way to stop a memory leak with a queue and a custom struct with a character array?

    Yes, use a std::string or std::vector<char>. Or, if you must manage the memory yourself, then read up on the Rule of 3/5/0 and implement these: