Qt: правильно настройте мой собственный QWidget

В Qt я пытаюсь настроить свой собственный QWidget, поэтому все должно работать хорошо из-за управления памятью и других вещей. Но я не могу понять все правильно с указателями, кучей и стеком. У меня есть виджет MyWidget, в котором есть QList с некоторыми объектами. Не могу понять как правильно все настроить.

Вы можете увидеть мой код ниже, и у меня есть несколько вопросов по этому поводу:

  1. Список переменных instace создается в куче, не лучше ли создать его в стеке?

  2. В моем списке есть указатели, было бы лучше просто создать объект в стеке и добавить его в список? (Чтобы у меня вообще не было указателей в списке)

  3. Когда я добавляю объекты в список, они автоматически получают список в качестве своего родителя? Итак, когда я удаляю список, все объекты внутри списка будут удалены?

  4. Цикл для каждого цикла, который я пытаюсь использовать, не работает, я получил «для этой операции ожидался тип указателя/массива вместо« int »»

  5. В своем коде я хочу создать другие виджеты, которые принимают объект из списка в качестве параметров. Это правильный способ сделать это, как я? Метод экземпляра MyOtherWidget выглядит следующим образом: MyOtherWidget(MyObject *myObject, QWidget *parent)

Спасибо за вашу помощь! Я новичок в Qt и C++, поэтому было бы здорово, если бы вы могли направить меня в правильном направлении. Как я могу настроить это правильно, чтобы упростить задачу, избежать утечек памяти и использовать как можно меньше памяти. Как бы вы установили то же самое?

Это мой код:

Мой виджет.h:

class MyWidget : public QWidget
{
Q_OBJECT

public:
    MyWidget(QWidget *parent = 0);
    ~MyWidget();

private:
    QList<MyObject*> *list;
};

Мой виджет.cpp:

MyWidget::MyWidget(QWidget *parent)
{
    ui.setupUi(this);

    list = new QList<MyObject*>();
    for (int i = 0; i<10; i++) 
    {
        MyObject *myObject = new MyObject("Hello",this);
        list->append(myObject);
    }

    foreach(MyObject *myObject, list)
    {
        //Lets say I want to create other widgets here and that they takes a MyObject as a parameter
        MyOtherWidget *myOtherWidget = new MyOtherWidget(myObject,this);
    }

}

MyWidget::~MyWidget(){
    delete list;
}

person Martin    schedule 29.03.2010    source источник


Ответы (3)


Объявление 1. Время жизни списка должно быть таким же, как время жизни экземпляра MyWidget, чтобы вы могли безопасно создавать список в стеке.

Объявление 2. Вы могли бы сделать это, но класс MyObject должен иметь конструктор по умолчанию, конструктор копирования и оператор присваивания (см. http://doc.trolltech.com/4.6/containers.html#container-classes для подробностей).

Ад.3. Право собственности на объект не передается при добавлении. Как и контейнеры STL, контейнеры Qt не вызывают удаление сохраненных указателей. Чтобы удалить все указатели, хранящиеся в QList (или другом контейнере Qt), вы можете использовать qDeleteAll(list). Имейте в виду, что вы, вероятно, не хотите делать это в опубликованном вами коде: вы передаете указатель MyWidget конструктору MyObject, и я предполагаю, что он затем используется в качестве родителя QObject. Таким образом, все объекты QObject будут удалены при удалении MyWidget.

Ад.4. Вторым аргументом макроса foreach должен быть контейнер, а не указатель на контейнер. Поэтому вам следует вызывать foreach(MyObject *obj, *list), если ваша переменная списка является указателем на QList.

Ад.5. Вы должны быть в порядке, пока MyOtherWidget не удаляет переданный MyObject (поскольку MyWidget уже является родителем MyObject, и вы в конечном итоге удалите один и тот же объект дважды).

Это грубое упрощение, но вы должны попытаться написать свой код таким образом, чтобы вам вообще не нужно было вызывать удаление. Создавайте вещи в стеке или полагайтесь на механизм родитель-потомок Qt (т.е. родители удаляют своих потомков). Позже вы, возможно, захотите прочитать об интеллектуальных указателях (QSharedPointer, QScopedPointer и т. д.).

РЕДАКТИРОВАТЬ:

Установлен ли родитель MyObject или нет, зависит от того, что вы делаете в конструкторе MyObject. Если вы передаете родительский аргумент конструктору QObject, то ваш конструктор Myobject выглядит так:

MyObject(const QString &text, QObject *parent = 0) : QObject(parent)
{
// more code...
}

родитель будет установлен, потому что это будет сделано в конструкторе QObject, который будет вызываться из-за кода ":QObject(parent)". Что делать, если у вас нет этого фрагмента? Поскольку MyObject наследует QObject, и вы не указываете, какой конструктор должен называться конструктором QObject по умолчанию, то есть будет вызываться QObject(QObject *parent = 0), поэтому родитель вашего MyObject будет NULL, и он не будет удален.

Я бы попытался избежать явной установки родителя с помощью метода setParent - для основных случаев использования установки родителя в конструкторе должно быть достаточно.

Старайтесь использовать правильную терминологию (не "метод экземпляра", а "конструктор"), читайте документацию Qt, руководствуйтесь здравым смыслом и старайтесь не думать, что что-то будет сделано автоматически. Родитель не устанавливается «автоматически» только потому, что вы называете один аргумент «родительским» — он устанавливается, потому что есть фрагмент кода, который делает это в конструкторе QObject, и вы несете ответственность за вызов передать правильный родитель конструктору QObject в классах которые наследуют QObject.

person chalup    schedule 29.03.2010
comment
Спасибо! Еще один вопрос, метод экземпляра MyObject выглядит как MyObject(QString *string, QObject *parent = 0). Когда я использую новый MyObject(Hello,this); Нужно ли мне иметь this-›setParent(parent) в методе экземпляра MyObject? Или родитель будет установлен автоматически? Если я использую setParent() в методе экземпляра, что произойдет, если я оставлю родителя пустым? Извините за все вопросы, но ваши ответы очень помогли! - person Martin; 29.03.2010

Да, проблема в том, что вы удаляете объект списка, но не его элементы!

Предлагаю посмотреть:

QList<Employee *> list;
list.append(new Employee("Blackpool", "Stephen"));
list.append(new Employee("Twist", "Oliver"));

qDeleteAll(list.begin(), list.end());
list.clear();

Подробнее здесь

Я также хотел бы спросить, действительно ли вам нужен указатель на ваш список? Вы можете просто иметь простой:

QList<MyObject*> list;

Следовательно, у вас на одну возможную утечку памяти меньше!

Надеюсь, что это помогает немного !

Изменить:
3. Ваши объекты MyObject имеют "это" в качестве родителей. Список не становится владельцем объектов, когда вы имеете дело с указателями.
4. Что касается цикла, возможно, вам следует подумать об Итераторах, посмотрите здесь qthelp://com.trolltech.qt.460/qdoc/qlist.html.

person Andy M    schedule 29.03.2010
comment
Если я не использую указатель на свой список, а просто использую список QList‹MyObject*›. Когда этот объект будет удален? Большое спасибо за вашу помощь! - person Martin; 29.03.2010
comment
Ну, это зависит... Если элементы списка имеют MyWidget в качестве родителя, то они будут удалены при удалении их родителя... Если у них нет родителей, вам придется удалить их в деструкторе MyWidget.. Помните, когда вы имеете дело с указателями, все зависит от того, кому принадлежит этот указатель?... Владелец занимается удалением его указателей... - person Andy M; 29.03.2010
comment
Но если я не использую указатель для списка, как вы рекомендовали выше. Когда список будет удален? Объекты в списке, как я понимаю, будут удалены в зависимости от их родителя. Но как насчет списка? - person Martin; 29.03.2010
comment
Ну, так как список является членом экземпляра класса MyWidget... Он будет удален при удалении объекта... Но опять же, будет удален объект списка, а не его содержимое (если оно содержит указатели)! - person Andy M; 29.03.2010
comment
Хорошо, тогда я понимаю. Другой вопрос, касающийся тех же вещей: если я использую QGraphicsScene::addItem(item), будет ли родительский элемент этого элемента установлен в QGraphicsScene, чтобы при удалении сцены элемент оставался таким же? У меня также есть тот же вопрос о QLayout::addItem ( item)? Это где-нибудь в документах Qt? - person Martin; 29.03.2010
comment
В документации Qt вы должны посмотреть QGraphicsScene::removeItem, вы можете найти Удаляет элемент элемента и все его дочерние элементы со сцены. Право собственности на элемент передается вызывающей стороне (т. е. QGraphicsScene больше не будет удалять элемент при уничтожении). Это было непросто :) - person Andy M; 29.03.2010
comment
Говоря о QLayout::addItem, прямо сказано: Примечание. Право собственности на элемент передается макету, и макет несет ответственность за его удаление.... Но есть также эта функция. обычно не вызывается в коде приложения. Чтобы добавить виджет в макет, используйте функцию addWidget(); чтобы добавить дочерний макет, используйте функцию addLayout(), предоставляемую соответствующим подклассом QLayout.... Поэтому я бы последовал их советам! - person Andy M; 29.03.2010

Вам не нужно хранить дочерние виджеты в списке, если вы сделаете их родителями текущего виджета. (Обычно вы создаете свои виджеты в стеке с новыми).

Qt имеет функцию автоматической очистки, что означает, что при удалении виджета удаляются все дочерние виджеты (виджеты, родителем которых является удаляемый виджет).

Таким образом, единственное, что вам нужно будет сделать (особенно для временных всплывающих виджетов), это стереть/удалить виджет «всплывающее окно» или как бы вы ни называли свой всплывающий виджет.

Вот и все.

person drahnr    schedule 29.03.2010