0

I am not experienced in OOP. I am developing an application using C++ and Qt. I have implemented 2 classes, base one and the one that inherits from it. Then I have added virtual methods for both and everything worked. But then I realized that I don't think it should... Here is the example:

This is my base class :

namespace Ui {
class CGenericProject;
}

class CGenericProject : public QDialog
{
    Q_OBJECT

public:
    explicit CGenericProject(QWidget *parent = 0);
    ~CGenericProject();

    EMeasures_t type();

private:
    Ui::CGenericProject *ui;

    virtual void initPlot();

protected:
    QCustomPlot* customPlot;
    QVector<double> m_x;
    QVector<double> m_y;

    EMeasures_t m_type;
};

It has a virtual method called initPlot and it looks like this:

void CGenericProject::initPlot()
{
    customPlot = ui->workPlot;

    customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes );
    customPlot->setFocusPolicy(Qt::ClickFocus);
    customPlot->xAxis->setAutoTickStep(false);
    customPlot->yAxis->setAutoTickStep(false);
    customPlot->xAxis->setTickStep(100);
    customPlot->yAxis->setTickStep(100);
    customPlot->xAxis->setRange(0, 1000);
    customPlot->yAxis->setRange(0, 1000);
}

And then i have a class that derives it:

class CEisProject : public CGenericProject
{
public:
    CEisProject();
    ~CEisProject();

private:
    virtual void initPlot();
    void exampleEisMethod();
};

its initPlot is here:

void CEisProject::initPlot()
{
    // give the axes some labels:
    customPlot->xAxis->setLabel("Re [Ohm]");
    customPlot->yAxis->setLabel("- Im [Ohm]");

    customPlot->replot();
}

This is how i create the object:

CGenericProject* test = new CEisProject();

Now, when the initPlot() method is called, first the initPlot() from base class CGenericProject is called and then initPlot() from CEisProject is called. I wanted this functionality, where I can predefine some stuff in generic class and then add specific stuff in the childs. But when I think of it, shouldn't initPlot() be calles exclusevily? I mean, shouldn't the method be called from base class or child class, instead of both, one after another? I have noticed this after reading this answer.

Constructors:

    CGenericProject::CGenericProject(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::CGenericProject)
    {
        ui->setupUi(this);
        initPlot();

        m_x.clear();
        m_y.clear();
    }

CEisProject::CEisProject()
{
    m_type = EMeasures_t::eEIS;
    initPlot();
}
Community
  • 1
  • 1
Łukasz Przeniosło
  • 2,725
  • 5
  • 38
  • 74
  • 1
    Only the overriding function should be called (here, `CEisProject::initPlot`). If both are called on your code, there's something in your code you're not showing us. – Eran Jul 12 '15 at 11:21
  • Im giving you all I have regarding this. I am debbuging step by step and I see that at the `CEisProject` creation `initPlot` from `CGenericProject` is classed first. – Łukasz Przeniosło Jul 12 '15 at 11:24
  • 1
    *"I am not to expirienced in objective languages."* - Nitpick: C++ is not an object-oriented language. Object-oriented programming is just *one* of the tools offered by C++, and in recent years has lost a lot of appeal and importance in favour of new programming styles involving lambdas and functional programming. – Christian Hackl Jul 12 '15 at 12:07
  • @ChristianHackl Nitpick: OOP and functional programming are mostly orthogonal. You can do both at the same time. Let's not forget that C++ lambda is syntax sugar for creating an entire class, and then instantiating it. If that's not OOP, I don't know what is. – Kuba hasn't forgotten Monica Jul 12 '15 at 13:40
  • @KubaOber: While you can do both at the same time, they are often competing as well. If you look at everything that came with C++11, almost none of the innovations touched OOP concepts (I can only think of `final` and `override`). OOP is when a function's implementation is chosen at run-time, i.e. using virtual functions. Lambdas are something different. You could use lambdas to build your own "virtual function" mechanism. The anonymous class created by a lambda does not have virtual functions. – Christian Hackl Jul 12 '15 at 13:59
  • @ChristianHackl Although this is all off-topic, OOP is definitely not "when a function's implementation is chosen at run time". That's just polymorphism - an aspect of OOP. As soon as you have data structures that have actions on them (methods, messages, etc.), you have object orientation. The fact that the anonymous class created by a lambda is not polymorphic doesn't make it any less of an object. The modern C++ style, leveraging RAII, is pretty much OOP taken to its wonderful conclusion. Functional style programming in C++ with map/reduce etc. is all implemented in terms of OOP! – Kuba hasn't forgotten Monica Jul 13 '15 at 01:43
  • @KubaOber: This definition of OOP is usually too broad in C++. *"In the context of C++ [...], it means programming using class hierarchies and virtual functions"*. http://www.stroustrup.com/bs_faq.html#oop – Christian Hackl Jul 13 '15 at 05:02

2 Answers2

2

You did not show the definition of the constructors, just their declaration. But I'm pretty sure the constructor definitions contain the answer to your question.

You may not be aware that the derived class constructor calls the base class constructor before directing virtual functions to the derived class. So a virtual function called in the base class construction (of an object which will soon be derived class) gets the base class definition of that virtual function.

JSF
  • 5,281
  • 1
  • 13
  • 20
  • I have added constructors. So that means when I call a derived class the base class constructor is called as well? That makes sense now :P. – Łukasz Przeniosło Jul 12 '15 at 11:27
  • When you construct the derived class, it always constructs the base class first. As I assumed above, your base class constructor and derived class constructor **each** call initPlot(). Both constructors are executed and each calls its own initPlot. You should understand the behaviour of calling both is not coming from the fact that initPlot is virtual (your original question) but from the implied call to base class constructor in any derived class constructor. – JSF Jul 12 '15 at 11:32
  • However, this likely means you have a problem in your code. The usual reason to have such an init function is that sometimes you want to reinitialize a previously constructed object. When you reinitialize a derived object, you would **want** the base object to also be reinitialized. But that does not occur the way you coded it. If you add a call to the base init in the derived init, then reinitialize would work, but construction would init the base twice. So you may need to rethink the whole relationship of these functions. – JSF Jul 12 '15 at 11:38
  • But it does work the way i wanted. The base class creates stuff and children specifies them. – Łukasz Przeniosło Jul 12 '15 at 11:40
2

Also, your constructor should be like:

// File .h
CEisProject(QWidget *parent = 0);

// File .cpp
CEisProject::CEisProject(QWidget *parent) : CGenericProject(parent)
{
    ...
}

or you won't be able to parent your derived widgets.

Miki
  • 40,887
  • 13
  • 123
  • 202