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

使用QSharedDataPointer实现私有数据的正确拷贝运算符,避免拷贝销毁后崩溃

问题描述

我需要为包含QSharedDataPointer指向私有数据的类实现拷贝运算符,当前实现仅在不删除任一拷贝时正常工作。实际情况与预期不符,拷贝指针时引用计数未增加。观察到代码调用的是移动赋值运算符QSharedDataPointer<T> &QSharedDataPointer::operator=(QSharedDataPointer<T> &&other),但我需要调用的是const引用版本QSharedDataPointer<T> &QSharedDataPointer::operator=(const QSharedDataPointer<T> &o),尝试强制转换也没成功。

相关代码

main.cpp

#include <QtDebug>

#include "Nix.h"

int main( int , char **  )
{
  Nix *nix1 = new Nix(1, "Hello, I'm one");
  Nix *nix2 = new Nix();

  nix2 = nix1;

  nix2->add(2, "Hello, I'm two");

  Nix *nix3 = new Nix(*nix2);
  nix3->add(3, "Hello, I'm three");

  qWarning() << "nix1: " << nix2->display(1) << nix1->display(2) << nix1->display(3);
  qWarning() << "nix2: " << nix2->display(1) << nix2->display(2) << nix1->display(3);

  delete nix2; // 如果删除nix1,访问nix2会触发段错误

  qWarning() << "nix3: " << nix3->display(1) << nix3->display(2) << nix3->display(3);

  // 触发段错误:
  qWarning() << "nix1: " << nix1->display(1) << nix1->display(2) << nix1->display(3);

  return 0;
}

Nix.h

#include <QString>
#include <QSharedDataPointer>

class NixPrivate;

class Nix
{
public:
   Nix();
   Nix(int a, QString b);
   ~Nix();
   Nix(Nix const &other);
   Nix &operator=(const Nix &other);

  QString display(int x);
  void add(int a, QString b);

private:
  QSharedDataPointer<NixPrivate> d;
};

Nix.cpp

#include "Nix.h"

#include <QMap>
#include <QString>
#include <QSharedData>

class NixPrivate : public QSharedData
{

 public:
   QMap<int, QString> myMap;

};

Nix::Nix()
  : d(new NixPrivate())
{
}

Nix::Nix(int a, QString b)
  : Nix()
{
  d->myMap[a] = b;
}

Nix::~Nix()
{
}

Nix::Nix(Nix const &other)
  : Nix()
{
  d = other.d;
}

Nix &Nix::operator=(const Nix &other)
{
  d = other.d;

  return *this;
}

QString Nix::display(int x)
{
  if (d->myMap.contains(x))
    {
      return d->myMap.value(x);
    }
  else
    {
      return "not found";
    }
}

void Nix::add(int a, QString b)
{
  d->myMap[a] = b;
}
问题分析与解决方案

核心错误:指针直接赋值而非对象赋值

你代码里的nix2 = nix1;是指针赋值,不是调用Nix类的赋值运算符!这会让nix2指针直接指向nix1所指向的对象,原来nix2创建的对象内存泄漏,且两个指针指向同一个Nix实例。此时删除nix2就等于删除了nix1指向的对象,后续访问nix1自然触发段错误,和QSharedDataPointer的引用计数无关。

修正步骤

  1. 修正main中的赋值逻辑:把指针赋值改成对象赋值,即*nix2 = *nix1;,这样才会调用你实现的operator=,让QSharedDataPointer正确增加引用计数。
  2. 可选:移除手动实现的拷贝构造和赋值运算符:QSharedDataPointer本身已经实现了正确的拷贝语义(拷贝时自动增加引用计数),Qt会为Nix类生成默认的拷贝构造和赋值运算符,完全可以删掉你手动写的版本,避免冗余代码。

修正后的main.cpp关键部分

int main( int , char **  )
{
  Nix *nix1 = new Nix(1, "Hello, I'm one");
  Nix *nix2 = new Nix();

  // 修正:对象赋值,调用Nix::operator=
  *nix2 = *nix1;

  nix2->add(2, "Hello, I'm two");

  Nix *nix3 = new Nix(*nix2);
  nix3->add(3, "Hello, I'm three");

  qWarning() << "nix1: " << nix1->display(1) << nix1->display(2) << nix1->display(3);
  qWarning() << "nix2: " << nix2->display(1) << nix2->display(2) << nix2->display(3);

  delete nix2; // 此时只是减少引用计数,不会销毁共享数据,因为nix1和nix3还持有引用

  qWarning() << "nix3: " << nix3->display(1) << nix3->display(2) << nix3->display(3);
  qWarning() << "nix1: " << nix1->display(1) << nix1->display(2) << nix1->display(3); // 不再触发段错误

  delete nix1;
  delete nix3;

  return 0;
}

简化后的Nix.h(移除冗余的拷贝构造和赋值声明)

#include <QString>
#include <QSharedDataPointer>

class NixPrivate;

class Nix
{
public:
   Nix();
   Nix(int a, QString b);
   ~Nix();
   // 移除手动声明的拷贝构造和赋值运算符,Qt会生成默认版本

  QString display(int x);
  void add(int a, QString b);

private:
  QSharedDataPointer<NixPrivate> d;
};

简化后的Nix.cpp(移除手动实现的拷贝构造和赋值)

#include "Nix.h"

#include <QMap>
#include <QString>
#include <QSharedData>

class NixPrivate : public QSharedData
{
 public:
   QMap<int, QString> myMap;
};

Nix::Nix()
  : d(new NixPrivate())
{
}

Nix::Nix(int a, QString b)
  : Nix()
{
  d->myMap[a] = b;
}

Nix::~Nix()
{
}

// 移除手动实现的拷贝构造和operator=

QString Nix::display(int x)
{
  return d->myMap.contains(x) ? d->myMap.value(x) : "not found";
}

void Nix::add(int a, QString b)
{
  d->myMap[a] = b;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:04:58