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

时间:2013-08-19 13:23:26

标签: c++ qt malloc memcpy qgraphicsscene

我有几个不同的类派生自QGraphicsItem或其子代(如QGraphicsRectItem)。我现在需要复制这些类的选定对象,而不知道我确切地复制了哪一个。

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

  1. MainWindow.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. MainWindow.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;
    };
    
  3. 由QGraphicsView和QPushButton组成的GUI足以满足此示例。

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

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

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

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

    编辑: 如果可能的话,我想这样做而不修改从QGraphicsItem派生的类的代码,因为这是一个小组工作,我不想错过其他功能。

3 个答案:

答案 0 :(得分:5)

如果你看一下qgraphicsitem.h中的类定义,你会看到以下内容:

private:
    Q_DISABLE_COPY(QGraphicsItem)
    Q_DECLARE_PRIVATE(QGraphicsItem)

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

编辑: 我认为你被拒绝复制能力的原因是因为QGraphicsItem跟随Composite Design Pattern。创建具有子项的单个对象的副本将导致子项具有多个父项。为了解决这个问题,你不仅要复制你感兴趣的项目,还要复制子层次结构中的每个孩子。对于非常大的层次结构,这可能会成为非常耗时的操作。

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

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

答案 1 :(得分:3)

您确实希望使用复制构造函数或赋值运算符 not mallocmemcpy。这是你用C ++复制对象的方法:

QGraphicsItem copiedItem = *item; 

答案 2 :(得分:1)

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

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

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并添加了指针成员,那么您需要添加自己的复制构造函数以确保发生深层复制,而不是浅层复制。