You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Message结构体入队列后内存地址不一致及内存泄漏疑问

自定义Message对象入队列后的地址疑问与内存泄漏问题

问题描述

初始代码与疑问

我正在创建自定义对象Message并将其加入队列,这会调用拷贝构造函数,但memmove()后的地址与预期不符。初始代码如下:

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

我疑惑为何将对象入队列后获取的内存地址不同,地址难道不该一致吗?另外如果不定义~Message()执行delete[] data;是否会造成内存泄漏?

编辑1

若不在析构函数中delete确实会内存泄漏,但为何内存位置不同?

编辑2

我更新了类代码如下:

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

但输出仍显示data指向不同地址:

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

我认为仍存在问题,但不确定问题所在。


问题解答

1. 为什么data的地址不同?

这是因为你实现的是深拷贝的拷贝构造函数:

  • 在拷贝构造函数中,你明确调用了new char[this->size],为新对象的data分配了一块全新的内存空间,再用memmove把原对象的数据复制过去。
  • 每次调用拷贝构造函数(比如push入队列、front赋值给other_message时),都会创建一个独立的Message对象,每个对象的data都指向专属的堆内存块,地址自然不同。

如果你期望地址一致,那是浅拷贝的行为,但浅拷贝会导致多个对象共享同一块内存,析构时会重复释放同一块内存,引发未定义行为——你的深拷贝实现其实是正确的,刚好避免了这个风险。

另外注意初始代码中你打印的是&message.data:这是成员变量data本身的地址(也就是对象里存储指针的那个内存位置),每个Message对象是独立的,所以它们的成员变量data的地址肯定不同;编辑2中你打印的是data指向的堆内存地址,因为深拷贝分配了新内存,地址也会不同。

2. 关于内存泄漏

如果不定义析构函数,编译器会生成默认析构函数,但默认析构函数不会自动delete[] data——因为data是原始指针,编译器不知道它指向堆内存。当Message对象被销毁时,data指向的堆内存不会被释放,必然造成内存泄漏。你当前定义的析构函数是正确的,能避免这个问题。

3. 编辑2中的拷贝调用说明

输出里的两次Message(const Message& other)分别来自:

  • 第一次:message_queue.push(message)——把栈上的message拷贝到队列的内存空间中;
  • 第二次:Message other_message = message_queue.front()——把队列中的对象拷贝到栈上的other_message中。

如果想减少拷贝,可以改用std::move触发移动构造:

// 入队时用move转移所有权
message_queue.push(std::move(message));
// 取出时用move转移所有权
Message other_message = std::move(message_queue.front());

这样会调用移动构造函数,把原对象的data所有权转移给新对象,此时转移前后的data会指向同一块内存,但原对象的data会被置为nullptr,避免重复释放。


内容的提问来源于stack exchange,提问作者Zuiax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:53:14