3

I'm trying to understand C++ memory management. As far as I know, objects on stack are guaranted to live (if stack is now overflown) only in current block of code. If I call a function from this block and pass a link of a stack object to this function it must work because calling block is still alive.

What happens with stack objects if I start a new thread from current block of code? As far as I understand this block is considered as finished.

The problem is that stack variables live some time after block finishes so I can't understand if they are guaranted to live or not.

Here I have some code. It compiles and works well but I suppose that it is not guaranted to work.

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 file:

#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();
}

My questins are:

  • Are a and thread stack or global objects?
  • Is it OK to declare thread objects as I did or should I create them only in heap?
  • Are a and b objects guaranted to live in thread?
  • Are a and b objects guaranted to live when they (their references) return from thread?

I appreciate any help.

chm
  • 359
  • 2
  • 11
  • Stack objects live as long as the function that contains them lives - so for example, a stack object defined in `main` lives for the duration of the program. –  May 05 '18 at 19:39
  • 1
    "that stack variables live some time after block finishes" -- no they don't. That's the whole point. – Sam Varshavchik May 05 '18 at 19:41
  • 2
    Each thread gets its own stack. So object lifetimes on each stack are independent of each other. – Richard Critten May 05 '18 at 19:41

1 Answers1

3

Your MyClass has 2 instance variables: int a and MyThread thread. Lifetime of these is bound to MyClass'es lifetime. Hence, since you declare MyClass on stack in main function these two variables will live in the same scope.

Now, in MyClass'es constructor you initialize fields of MyThread with pointer to variable a which, as already shown, is a stack variable alive throughout the whole program runtime, and a local variable int b which will cease to exist (hence the pointer will be invalid and dereferencing it is an undefined behaviour) as soon as the constructor finishes running.

So:

  • a and thread are stack variables, but since their parent (MyClass instance) is declared in main scope of main function they will be valid for the whole duration of the program
  • I would say so, but I'm in no way Qt expert (quick search around the net suggests it's safe to declare Qt objects on stack as long as you know their lifetime).
  • No, b will go out of scope hence dereferencing pointer to it results in UB. Your program may work, but it's unsafe to do so.
  • What Qt guarantees is that what you pass as arguments to a signal is delivered to signal's receiver. From what I gathered by reading this article Qt will copy the arguments (so, if the arguments are classes they will be copy constructed) and dispatch the call to appropriate thread (if subscription was done from different thread than the one emiting the signal). This however does not prolong (or in any other way control) the lifetime of objects pointed to via your pointer - one should use shared_ptr<T> if you want guarantee that the object is delivered intact.
orhtej2
  • 2,133
  • 3
  • 16
  • 26