Search code examples
c++qtclassinheritanceclass-constructors

Qt inheritance and classes constructors confusion


I am a bit new to Qt C++ and I think there is a small thing I'm missing but can't figure what is it.

I am trying to make a simple Qt C++ application just to get familiar with it, but I face some problem, First, I have the votor class, which is the main application class, and another class which is called recorder, which will be used inside the main votor class. For simplicity, I omitted un-related parts, Here are the files:

votor.h

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_votor.h"

#ifndef TSTRECORD_H
#define TSTRECORD_H
#endif

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QWidget>
#include "recorder.h"


class VOTor : public QMainWindow
{
    Q_OBJECT

public:
    VOTor(QWidget *parent = Q_NULLPTR);
    recorder xs;

private:
    Ui::VOTorClass ui;

};

votor.cpp

#include "votor.h"

VOTor::VOTor(QWidget *parent) : QMainWindow(parent), xs(parent)
{
    ui.setupUi(this);
    //xs = recorder();

}

recorder.h

#pragma once
#include <QDebug> 
#include <QObject>
#include <QtCore/qbuffer.h>
#include <QtCore/qiodevice.h>

#include<QtMultimedia/qaudioformat.h>
#include<QtMultimedia/qaudiodeviceinfo.h>
#include<QtMultimedia/qaudioinput.h>

class recorder : public QObject
{
    Q_OBJECT

public:
//  recorder();
    recorder(QObject *parent);
    ~recorder();
        //Some functions related to recording omitted for more focus
private:
      //omitted members for simplicity, just some integers, chars and qt objects not related to problem

recorder.cpp

#include "recorder.h"

recorder::recorder(QObject *parent) : QObject(parent)

{
 //just initializing the omitted members normally
}
//recorder::recorder() {}

recorder::~recorder()
{

}

as you see, recorder object is a member inside votor class. Now, I need to call recorder constructor to initialize its parent. Now, I knew I can't just make (inside votor.h)

recorder xs(parent);

so, 1- is there a way I can call recorder constructor other than initialization list ? I want another way as I was more convenient to use recorder xs(..) than initialization list, I feel (just feel) that using initialization list is heavy (not performance-wise but on readability ). I know also I can use dynamic allocation, but I don't want to use it without a good reason for.

2- I have decided to use initialization list to call recorder constructor and pass (Qobject* parent) to recorder. The code is built successfully, but when running, it gives access violation error which I can't figure why... it gives: "Access violation reading location 0x7C32F08D."

I think I am missing small thing.. I hope to know what is wrong.

Edit: as @p-a-o-l-o suggested, the access violation was from the omitted code, So, I am posting it here as I don't know what is the problem in my code:

full version of recoder.h

class recorder : public QObject
{
    Q_OBJECT

public:
    recorder();
    //recorder(QObject *parent);
    ~recorder();
    void record();
    void stop();

public slots:
    void stateChanged(QAudio::State);

private:
    unsigned char state;
    QBuffer* voiceBuffer;
    QAudioFormat* format;
    QAudioDeviceInfo* info;
    QAudioInput* audioIn;

    unsigned char writeWav(QByteArray*);


};

and the part causes access violation according to debug mode, which is constructor of recorder class

recorder::recorder() : QObject(Q_NULLPTR)
{

    state = 0;
    voiceBuffer->open(QIODevice::WriteOnly); 
    format->setSampleRate(8000);
    format->setChannelCount(1);
    format->setSampleRate(16);
    format->setByteOrder(QAudioFormat::LittleEndian);
    format->setCodec("audio/pcm");
    format->setSampleType(QAudioFormat::SignedInt);

    *info =  QAudioDeviceInfo::defaultInputDevice();
    audioIn = &QAudioInput(*info, *format);
    //audioIn->stateChanged.connect(stateChanged);
    //connect(audioIn, &QAudioInput::stateChanged, stateChanged);
    connect(audioIn, SIGNAL(stateChanged (QAudio::State) ), this, SLOT(stateChanged(QAudio::State)));
}

Solution

  • About question #1: as rightfully suggested in comments, a proper constructor for a QObject-derived class would have a nullptr default argument:

    recorder(QObject *parent = Q_NULLPTR);
    

    If you really need that parent object to initialize the other members in construction, you have no alternatives and must call that constructor, somehow.

    Otherwise, have a no-arguments constructor and initialize the other members there:

    recorder::recorder() : QObject(Q_NULLPTR)
    {
        //just initializing the omitted members normally
    }
    

    Such a constructor will be invoked automatically, no need of initialization list here.

    If you still need a parent for the recorder object, give it one in the VOTor constructor:

    VOTor::VOTor(QWidget *parent) : QMainWindow(parent)
    {
        ui.setupUi(this);
        xs.setParent(parent);
    }
    

    About question #2: as far as I can see from the code you posted, the access violation have nothing to do with Qt parenting and must be related to the (omitted) code inside the recorder constructor.

    Just to clear it out: parenting a member object is always safe, since it will go out of scope before ~QObject() gets called, so it will be removed from the children list before ~QObject() could possibly call delete on it.

    Taking the OP code as an example, the sequence of destructors is the following:

    ~VOTor()
    ~recorder() ---> xs is removed from the children list
    .
    .
    .
    ~QObject() ---> will call delete on all children, but ws is not in the list anymore
    

    Qt documentation is quite clear about the order of construction/destruction of parents and children: in short, if the child is created on the stack everything will be fine until the child is created after the parent. But, again, if the child happens to be a member of the parent class, it will be fine as well for the reasons mentioned above (even if its construction actually happens before its parent's).