使用memcpy复制从QGraphicsItem派生的类不会创建新对象

Copying classes derived from QGraphicsItem using memcpy does not creat new object

本文关键字:新对象 创建 对象 复制 memcpy QGraphicsItem 派生 使用      更新时间:2023-10-16

我从QGraphicsItem或其子类(如QGraphicsRectItem)派生了几个不同的类。当我需要复制这些类的选定对象时,却不知道要复制哪一个。

由于QGraphicsScene::selectedItems()返回了一个所选项目的列表,我决定使用它,但是我不能复制QGraphicsItem,因为它是一个抽象类。为了解决这个问题,我尝试使用mallocmemcpy复制对象。

  1. 主窗口.cpp

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        scene = new QGraphicsScene();
        item = new QGraphicsRectItem(50,50,50,50);
        item->setFlag(QGraphicsItem::ItemIsSelectable);
        scene->addItem(item);
        item->setSelected(true);
        ui->graphicsView->setScene(scene);
    }
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    void MainWindow::on_pushButton_clicked()
    {
        for(QGraphicsItem *item : scene->selectedItems())
        {
            QGraphicsItem *copiedItem=(QGraphicsItem *)malloc(sizeof(*item));  
            memcpy(copiedItem, item, sizeof(*copiedItem));   
            copiedItem->moveBy(50, 50);
            scene->addItem(copiedItem);
            qDebug() << item;
            qDebug() << copiedItem;
        }
    }
    
  2. 主窗口.h

    namespace Ui {
    class MainWindow;
    }
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
        public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        QGraphicsScene *scene;
        QGraphicsRectItem *item;
        private slots:
            void on_pushButton_clicked();
        private:
            Ui::MainWindow *ui;
    };
    

由QGraphicsView和QPushButton组成的GUI就足够用于此示例了。

这段代码似乎有效,itemcopiedItem有不同的地址,但与qDebug()返回的属性相同。

然而,该场景返回以下错误:

QGraphicsScene::addItem: item has already been added to this scene

我不太明白为什么场景认为这些物品是一样的,而它们有不同的地址。有什么办法解决这个问题吗?

编辑:如果可能的话,我想在不修改从QGraphicsItem派生的类的代码的情况下完成这项工作,因为这是一项小组工作,我不想bug其他功能。

如果您查看qgraphicitem.h中的类定义,您将看到以下内容:

private:
    Q_DISABLE_COPY(QGraphicsItem)
    Q_DECLARE_PRIVATE(QGraphicsItem)

这意味着,在设计中,不应该复制QGraphicsItem,每个对象都应该是唯一的。

编辑:我想,您被拒绝复制能力的原因是因为QGraphicsItem遵循复合设计模式。创建具有子项的单个对象的副本会导致子项具有多个父项。为了绕过这一点,你不仅要复制你感兴趣的项目,还要复制子层次结构中的每个子项。对于非常大的层次结构,这可能会成为一项非常耗时的操作。

如果您真的觉得需要进行复制,可以创建一个克隆工厂函数/类,通过遍历对象的所有属性并将其传输到新创建的QGraphicsItem,来创建QGraphicsIItem及其所有子项的克隆。

如果这不可行,也许可以考虑用不同的方式来实现你的目标。

您确实希望使用复制构造函数或赋值运算符,而不是mallocmemcpy。这就是在C++中复制对象的方法:

QGraphicsItem copiedItem = *item; 

通过使用malloc块复制类的内存来复制类的问题是,如果类包含指向对象或数组的指针,那么只会发生浅层复制。

在获取指向QGraphicsItem的指针的情况下,您需要确定要复制的项的类型(它的实际子类,而不是基类),并使用复制构造函数。QGraphicsItem包含一个名为type()的函数,该函数返回一个int,指示它是哪个项。您也可以通过实现type()函数在自己的派生类中添加该函数。例如,来自Qt文档:-

class CustomItem : public QGraphicsItem
{
   ...
   enum { Type = UserType + 1 };
   int type() const
   {
       // Enable the use of qgraphicsitem_cast with this item.
       return Type;
   }
   ...
};

或者,如果所有的类都是您自己的类型,您可以使用自己的系统。一旦你知道了类型,你就可以用它的复制构造函数复制项目:-

// Example, assuming type denotes a QGraphicsItemRect    
QGraphicsItemRect rect = (*originalRect);

请注意,如果您从QGraphicsItem继承并添加了作为指针的成员,则需要添加自己的复制构造函数,以确保发生深度复制,而不是浅层复制。