Questions about OOP

 

I created this topic in order to avoid creating a separate topic for each OOP question (I have already created several of these).

 

With what access specifier should constructors of an abstract class be declared?

In the documentation, the constructor of an abstract class is declared with the access specifier public:

https://www.mql5.com/en/docs/basis/oop/abstract_type

class CAnimal
  {
public:
                      CAnimal();     // Constructor
   virtual void       Sound() = 0;   // A pure virtual function
private:
   double             m_legs_count;  // The number of the animal's legs
  };

But what's the point if the abstract class cannot be instantiated?

Wouldn't it be more logical to declare an abstract class constructor with a protected access specifier?

class A
  {
protected:
               A(int value) : m_value(value) {}
   int         m_value;
public:
   virtual int method() = 0;
  };

class B : public A
  {
public:
       B() : A(0) {}
   int method() override { return(++m_value); }
  };

void OnStart()
  {
   B b;
   Print(b.method());
  }
 

Is it possible to define the constructor body after the class description if an initialization list is used?

Here is an example without an initialization list:

class Foo
  {
private:
   int m_a;
   int m_b;
   int m_c;
public:
   Foo(int a, int b);
  };

Foo::Foo(int a,int b) // Defining the constructor body after the class description
  {
   m_a = a;
   m_b = b;
   m_c = a + b;
  }

Now let's add the initialization list:

class Foo
  {
private:
   const int m_a;
   const int m_b;
   int       m_c;
public:
   Foo(int a, int b) : m_a(a), m_b(b)
     {
      // Can this constructor body be defined after the class description?
      m_c = a + b;
     }
  };
 
Vladislav Boyko #:

With what access specifier should constructors of an abstract class be declared?

In the documentation, the constructor of an abstract class is declared with the access specifier public:

But what's the point if the abstract class cannot be instantiated?

Wouldn't it be more logical to declare an abstract class constructor with a protected access specifier?

It doesn't make a difference.

In both cases you are prevented from instancing the base class.

Access specifiers as well as abstract classes are compiler level concepts, helping the coder to use it as intended. None of them makes it into the binary representation.

Same or similar goes for keyword "const", there is no binary representation. Also namespace, override, final, delete (not the object delete, but the method delete) - Not sure I mentioned all.


 
Vladislav Boyko #:

Is it possible to define the constructor body after the class description if an initialization list is used?

Here is an example without an initialization list:

Now let's add the initialization list:

Yes, that is possible. You specify it just like you do inside the class declaration. There is no difference.
 
Dominik Egert #:
It doesn't make a difference.

In both cases you are prevented from instancing the base class.

Access specifiers as well as abstract classes are compiler level concepts, helping the coder to use it as intended. None of them makes it into the binary representation.

Same or similar goes for keyword "const", there is no binary representation. Also namespace, override, final, delete (not the object delete, but the method delete) - Not sure I mentioned all.

Thanks a lot!

Dominik Egert #:
Yes, that is possible. You specify it just like you do inside the class declaration. There is no difference.

Do I understand correctly?

class Foo
  {
private:
   const int m_a;
   const int m_b;
   int       m_c;
public:
             Foo(int a, int b);
   void      print() { PrintFormat("a %i, b %i, c %i", m_a, m_b, m_c); }
  };

Foo::Foo(int a, int b) : m_a(a), m_b(b)
  {
   m_c = a + b;
   print();
  }

void OnStart()
  {
   Foo a(1, 2);
  }
 
Dominik Egert #:
It doesn't make a difference.

In both cases you are prevented from instancing the base class.

Access specifiers as well as abstract classes are compiler level concepts, helping the coder to use it as intended. None of them makes it into the binary representation.

Same or similar goes for keyword "const", there is no binary representation. Also namespace, override, final, delete (not the object delete, but the method delete) - Not sure I mentioned all.

So the answer is yes it's more logical to use a protected constructor for abstract classes. We could even have argue it should not be allowed by the compiler to use a public constructor for an abstract class, isn't ?

I don't get your point. Any programming language, as MQL in our case, is "compiler level concepts, helping the coder to use as intended". What do you mean by the second part about binary representation ?

 
Alain Verleyen #:

So the answer is yes it's more logical to use a protected constructor for abstract classes. We could even have argue it should not be allowed by the compiler to use a public constructor for an abstract class, isn't ?

I don't get your point. Any programming language, as MQL in our case, is "compiler level concepts, helping the coder to use as intended". What do you mean by the second part about binary representation ?

What I mean:

Qualifiers like private, protected, public or const, namespace and alike are only effective inside the high level language, like MQL, C, C++ and alike.

You do not have such concepts in assembly, or binary respectively.

Object oriented design is only a high level language concept, it does not exist in the actual binary code which is executed on the CPU. So are also all other concepts, like private, const, namespace and alike.

Although, v-tables are actually implemented on assembly level and therefore also in binary, it is actually just a dynamic lookup of the call/jump instruction. Maybe comparable to a switch statement to some extend.

So, what I mean is, these conceptual design "helpers" are actually only on compiler level, and produce only feedback to the coder, knowing, he has assembled his code correctly and as intended. If we don't comply to these (by the coder) coded and implemented rules, we get an error from the compiler. It will not produce binary executables, but ask the coder to fix the improper code.

 
Vladislav Boyko #:

Thanks a lot!

Do I understand correctly?

Yes, if I am not mistaking...
 

I noticed one interesting feature.

A parametric constructor allows the use of a private destructor:

class Foo
  {
public:
   Foo(int) { Print(__FUNCSIG__); }
   Foo()    { Print(__FUNCSIG__); }
private:
   ~Foo()   { Print(__FUNCSIG__); }
  };

void OnStart()
  {
   Foo obj1(1); // OK
   Foo obj2;    // 'Foo::~Foo' - cannot access private member function
  }

That is, the following program will compile and work:

void OnStart()
  {
   Foo obj1(1);
  }

We can even use the operator new:

void OnStart()
  {
   Foo* obj1 = new Foo(1); // OK
  }

But this object cannot be deleted:

void OnStart()
  {
   Foo* obj1 = new Foo(1); // OK
   delete obj1;            // 'obj1' - cannot access private member function
  }

This feature is interesting because the ability to create an instance depends on which constructor is used (parametric or default)


Dominik Egert #:
Yes, if I am not mistaking...

I checked and it works, thanks!

I would certainly like this syntax to be documented. Because I can't be sure it will always work unless it's documented. But I'm saying this in general. I think this constructor body definition will always work

 
Vladislav Boyko #:

I noticed one interesting feature.

A parametric constructor allows the use of a private destructor:

That is, the following program will compile and work:

We can even use the operator new:

But this object cannot be deleted:

This feature is interesting because the ability to create an instance depends on which constructor is used (parametric or default)


I checked and it works, thanks!

I would certainly like this syntax to be documented. Because I can't be sure it will always work unless it's documented. But I'm saying this in general. I think this constructor body definition will always work

Now that is interesting.... - Dont know if thats supposed to be like that. I would guess no. - Maybe MQ as screwed up the access operators (again).
Reason: