C ++ вопросы управления памятью стека - PullRequest
0 голосов
/ 05 мая 2018

Я пытаюсь понять управление памятью в C ++. Насколько я знаю, объекты в стеке гарантированно живут (если стек теперь переполнен) только в текущем блоке кода. Если я вызываю функцию из этого блока и передаю ссылку на объект стека на эту функцию, она должна работать, потому что вызывающий блок еще жив.

Что происходит с объектами стека, если я запускаю новый поток из текущего блока кода? Насколько я понимаю, этот блок считается законченным.

Проблема в том, что переменные стека живут некоторое время после завершения блока, поэтому я не могу понять, гарантированно они живут или нет.

Здесь у меня есть код. Он компилируется и работает хорошо, но я полагаю, что он не гарантированно работает.

main.h:

#include <QObject>
#include <QThread>

#ifndef MAIN_H
#define MAIN_H

class MyThread : public QThread
{
    Q_OBJECT

    virtual void run();

signals:
    void returnVar(int *aPtr, int *bPtr);

public:
    int *a;
    int *b;
};

class MyClass : public QObject
{
    Q_OBJECT

    int a; // Is it considered stack or global?

    void someFunc(int *aPtr, int *bPtr);
    MyThread thread; // Is it OK to create thread objects like this or should I use heap only?

public:
    MyClass();

public slots:
    void varAdded(int *aPtr, int *bPtr);
};

#endif // MAIN_H

.cpp файл:

#include <QCoreApplication>
#include <QDebug>
#include "main.h"

void MyThread::run()
{
    qDebug() << "A in thread: " << *a << ", B in thread: " << *b;

    emit returnVar(a, b);
}

MyClass::MyClass()
{
    a = 1;

    int b = 2;

    someFunc(&a, &b);

    //MyThread thread; // If i declare thread here program will crush because thread was destroyed while running
    QObject::connect(&thread, SIGNAL(returnVar(int*, int*)), this, SLOT(varAdded(int*, int*)));
    thread.a = &a;
    thread.b = &b;

    // Is current block considered alive when I start a thread?
    // As far as I understand it it not alive any more. Am I right?
    thread.start();

    // If I give this block some time I can create stack thread object in constructor and it will work
    //std::this_thread::sleep_for(std::chrono::milliseconds(5));
}

void MyClass::someFunc(int *aPtr, int *bPtr)
{
    // As far as I understand these objects will work fine anyway because calling block is alive.
    // Am I right?
    qDebug() << "A: " << *aPtr << ", B: " << *bPtr;
}

void MyClass::varAdded(int *aPtr, int *bPtr)
{
    qDebug() << "A returned from thread: " << *aPtr << ", B returned from thread: " << *bPtr;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    MyClass myClass;
    return a.exec();
}

Мои квестины:

  • Являются ли и thread стеком или глобальными объектами?
  • Можно ли объявлять объекты потоков так, как я это делал, или я должен создавать их только в куче?
  • Гарантируется ли a и b объекты в потоке?
  • Гарантируется ли a и b объекты, когда они (их ссылки) возвращаются из потока?

Я ценю любую помощь.

1 Ответ

0 голосов
/ 06 мая 2018

Ваш MyClass имеет 2 переменных экземпляра: int a и MyThread thread. Продолжительность их жизни связана с продолжительностью жизни MyClass. Следовательно, поскольку вы объявляете MyClass в стеке в функции main, эти две переменные будут находиться в одной области видимости.

Теперь в конструкторе MyClass вы инициализируете поля MyThread указателем на переменную a, которая, как уже показано, является переменной стека, работающей в течение всего времени выполнения программы, и локальной переменной int b который перестанет существовать (следовательно, указатель будет недействительным, а разыменование - неопределенное поведение), как только закончится работа конструктора.

Итак:

  • a и thread являются переменными стека, но поскольку их родитель (MyClass instance) объявлен в основной области действия функции main, они будут действительны в течение всей продолжительности программы
  • Я бы сказал так, но я ни в коем случае не эксперт Qt (быстрый поиск в сети показывает, что безопасно объявлять объекты Qt в стеке, если вы знаете их время жизни).
  • Нет, b выйдет из области видимости, поэтому разыменование указателя на него приведет к UB. Ваша программа может работать, но это небезопасно.
  • Qt гарантирует, что то, что вы передаете в качестве аргументов сигнала, доставляется получателю сигнала. Из того, что я собрал, прочитав эту статью Qt скопирует аргументы (поэтому, если аргументы являются классами, они будут созданы с помощью копирования) и отправит вызов соответствующему потоку (если подписка была сделана из другого потока, чем один испускающий сигнал). Это, однако, не продлевает (или каким-либо другим образом управляет) время жизни объектов, на которые указывает ваш указатель - следует использовать shared_ptr<T>, если вы хотите гарантировать, что объект доставлен в целости.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...