inheritance confusion

Feb 16, 2023 at 5:07pm
Hi guys,

assume I have a parent class which is supposed to be abstract in the sense that any running program would only run a child class that inherited from parent.

Some pseudocode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class parent{
    method A(){}  \\ empty method to be overwritten by childs
    method B(){
               \\ code block that stays the same for all childs
                A() 
               \\ code block that stays the same for all childs
}

class child : public parent{
     method A(){\\ code of the method}
}

int main(){

child testobjekt;
testobject.B();

}


I want to have many child classes with their own method A.

Is something like this possible? The thing is that method B in the parent class contains lots of constant code that would be the same for all childs so I do not want to have to copy'n'paste it everytime. But B in the parent class tries to call A which will only be implemented for the child classes..

Best,
PiF
Feb 16, 2023 at 5:34pm
Feb 17, 2023 at 12:08am
Is something like this possible?

Yes!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <cstdio>

class base
{
public:
  // "= 0": this function is pure virtual and must be implemented by derived classes.
  virtual void f() = 0; 

  void g()
  {
    std::puts("starting base::g"); 
    f();          
    std::puts("ending base::g");
  }    
};

class derived1: public base
{
public:
  virtual void f() override { std::puts("derived1::f"); }
};

class derived2: public base
{
public:
  virtual void f() override { std::puts("derived2::f"); }
};

int main()
{
  derived1 d1; 
  d1.g();
}

https://godbolt.org/z/1n45hvzMz
Last edited on Feb 17, 2023 at 12:09am
Feb 17, 2023 at 9:42am
Small point - virtual overriden member functions don't need virtual to be specified. It's sufficient to just mark the base function as virtual.

1
2
3
void f() override {
    std::puts("derived1::f");
}

Feb 17, 2023 at 12:01pm
https://en.cppreference.com/w/cpp/language/virtual explains several details with examples.
Feb 17, 2023 at 4:54pm
Also see chapters 16, 17, 18 of:
https://www.learncpp.com
Last edited on Feb 17, 2023 at 4:54pm
Feb 17, 2023 at 7:28pm
Hi guys,

I had actually learned about virtual functions a couple of weeks ago, but seemed to have forgotten most of it again ~~
Goes to show that one should always write code when learning new programming concepts.

Thanks, I try it out!
Feb 18, 2023 at 11:05am
I had actually learned about virtual functions a couple of weeks ago, but seemed to have forgotten most of it again

It may be useful to remember that C++ supports more than one programming paradigm. When doing object oriented programming, object methods are always virtual functions used from a pointer or reference.

If there's no virtual method or the object is being used directly, it's not an object, in the Object Oriented Programming's (OOP) view of an object.

I didn't want to clutter the main idea, so I'll add this here at the end. When talking about OOP in C++, it's natural to use the language's built in support of runtime or dynamic polymorphism, but there are ways of implementing compile time or static polymorphism, that have improved runtime performance, but are more complicated to read.
Last edited on Feb 18, 2023 at 11:12am
Feb 21, 2023 at 1:27pm
Make the function that needs to be overridden pure. In such a way, you are 'forcing' the object to be valid (compile time errors, if the pure function is not implemented!). Like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>

class Parent{
    public:
        Parent() = default;
        virtual void A() = 0; // make it a pure function so that all base
                              // classes must implement it. Otherwise, compile time error
        void B(){
            std::cout << "The parent class has the implementation of this method\n";
        }

        virtual ~Parent()=default;
    private:
    // private variables and functions
};

class FirstChild: public Parent{
    public:
        virtual void A() override{
            std::cout << "FirstChild has implemented A() in its own way\n";
        }
};

class SecondChild: public Parent{
    public:
        virtual void A() override{
            std::cout << "SecondChild has implemented A() in its own way\n";
        }
};

class ThirdChild: public Parent{
    public:
        virtual void A() override{
            std::cout << "ThirdChild has implemented A() in its own way\n";
        }
};

int main(){
    FirstChild first;
    first.B();
    first.A();

    std::cout << std::string(50, '=') << std::endl;

    SecondChild second;
    second.B();
    second.A();

    std::cout << std::string(50, '=') << std::endl;
    ThirdChild third;
    third.B();
    third.A();


    return 0;
}

Last edited on Feb 21, 2023 at 1:29pm
Feb 21, 2023 at 9:42pm
Hi guys,

can you tell me why in the last line the method of the child class is executed?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

class base_class {
    public:
    
        virtual void collect_measurements() const = 0;
    
        void run_simulation() const {
            collect_measurements();
        }

};

inline void base_class::collect_measurements() const {std::cout<< "Calling collect_measurements from base_class\n";}


class derived_class : public base_class {
    public:
    
        virtual void collect_measurements() const override{
            std::cout<< "Calling collect_measurements from derived class\n";
        }

};


int main () {
    
    derived_class d;
    
    d.run_simulation();                         // gives implementation of derived class as expected 
    d.base_class::collect_measurements();       // gives standard implementation as expected
    d.base_class::run_simulation();             // gives implementation of derived class... why??
    
}
Last edited on Feb 21, 2023 at 9:42pm
Feb 21, 2023 at 10:13pm
You do call member function void run_simulation() const. It is non-virtual and member of the base_class. It does what it does.

You could call the very same function with d.derived_class::run_simulation(); and the result would be exactly the same. (The run_simulation() is a member due to inheritance.)

Calling the run_simulation() more explicitly does not affect what the implementation of that function does. The implementation is:
1
2
3
{
    collect_measurements();
}

and that calls a virtual member function.
Feb 21, 2023 at 10:41pm
Thanks Keskiverto,

then my way of thinking is wrong.
I interpret
 
d.base_class::run_simulation();

as "operating on an object of base_class" and thus base_class::collect_measurements() should be called.

On another note, my book says that calling of a virtual function as opposed to a nonvirtual one only shows different behavior when it is called from a reference or pointer of static type base_class.
As I do not use pointers or references this must be an error.

I remove the virtual keyword and check what happens in the d.run_simulation() case which resolved to the derived class above:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

class base_class {
    public:
    
        void collect_measurements() const;
    
        void run_simulation() const {
            collect_measurements();
        }

};

inline void base_class::collect_measurements() const {std::cout<< "Calling collect_samples from base_class\n";}


class derived_class : public base_class {
    public:
    
        void collect_measurements() const {
            std::cout<< "Calling collect_measurements from derived class\n";
        }


};


int main () {
    
    derived_class d;
    
    d.run_simulation();   // gives base implementation (run_simulation was not overwritten
                          // so base_class::run_simulation() is called which then calls collect_simulation() in its scope.)   
        
}
Feb 22, 2023 at 2:27am
I interpret d.base_class::run_simulation(); as operating on an object of base_class and thus base_class::collect_measurements() should be called.

You're operating on d, whose type is derived_class. You're just calling a particular member function by its full name, which is base_class::run_simulation.

On another note, my book says that calling of a virtual function as opposed to a nonvirtual one only shows different behavior when it is called from a reference or pointer of static type base_class.
As I do not use pointers or references this must be an error.
You are using references, just not explicitly. The implicit object parameter of base_class::collect_measurements is an lvalue reference to base_class const. That's why the call to collect_measurements on line 9 is dynamically dispatched according to the dynamic type of *this.
See: https://cplusplus.com/forum/beginner/284191/#msg1230640
Last edited on Feb 22, 2023 at 2:30am
Feb 22, 2023 at 9:21am
my book says


Which book are you using?

A derived class with public inheritance has access to public and protected members of the class from which it is derived which in turn has access to its....

See
https://www.learncpp.com/cpp-tutorial/inheritance-and-access-specifiers/

and other chapters re classes.
Last edited on Feb 22, 2023 at 12:56pm
Mar 9, 2023 at 11:32pm
I am still a bit confused by the logic. Let's move away from virtual functions again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

class base_class {
    public:
    
        void collect_measurements() const;
    
        void run_simulation() const {
            collect_measurements();
        }

};

inline void base_class::collect_measurements() const {std::cout<< "Calling collect_samples from base_class\n";}


class derived_class : public base_class {
    public:
    
        void collect_measurements() const {
            std::cout<< "Calling collect_measurements from derived class\n";
        }

};


int main () {
    
    derived_class d;
    
    d.run_simulation();                         // gives base implementation
                                                      
}


Since run_simulation was not overwritten, d.run_simulation() is the same as d.base_class::run_simulation(). collect_measurements(), however, was overwritten and should be shadowing base_class::collect_measurements(). Therefore, even though collect_measurements() is called by a member function of the base class, I had expected it to resolve to derived_class::collect_measurements() as we are still operating on an object of type derived_class.

But it seems to be the case that member functions of the base class, even if they are called by an object of the derived class, only see the member functions of the base class, even if the latter were overwritten in the derived class. Is this true?
Last edited on Mar 9, 2023 at 11:33pm
Mar 10, 2023 at 1:28am
I am still a bit confused by the logic. Let's move away from virtual functions again.
Consider this:
1
2
3
4
5
6
7
int main()
{  
  derived_class const cd{}; 
  base_class const* pcd = &cd
  // calls base_class::collect_measurements because it's nonvirtual
  pcd->collect_measurements(); 
}
pcd has a static type of base_class const* and a dynamic type of derived_class const*. Since base_class::collect_measurements isn't virtual, the static type is used to choose the right function, and the base class version is selected.

The same reasoning applies to your code on line 9:
6
7
8
9
10
11
class base_class {
  // ...
  void run_simulation() const {
    collect_measurements();
  }
}
On line 9, collect_measurements() is shorthand for this->collect_measurements(). And this has a static type of base_class const* (because it's a member of base_class) and a dynamic type of derived_class const* (because run_simulation was called through a pointer to derived_class).

Since base_class::collect_measurements isn't virtual, the static type of this is used and the base class implementation gets called. If you wanted to use the dynamic type instead you'd need to make the function virtual.
Last edited on Mar 10, 2023 at 5:57am
Mar 10, 2023 at 8:45pm
Thanks mbozzi!

So if I have
1
2
3
4
int main () {
    derived_class d;
    d.run_simulation();                                                    
}

the collect_measurements() in
1
2
3
4
5
6
class base_class {
  // ...
  void run_simulation() const {
    collect_measurements();
  }
}

is actually the same as this->collect_measurements(), and since we call it from run_simulation(), a member of the base class, this is a pointer of static type base_class. Since I operate on an object of type derived_class, the dynamic type of this will be derived_class. But since collect_measurements is not virtual, this has no consequence and we obtain base_class::collect_measurements.

Now, if I wanted
1
2
3
4
int main () {
    derived_class d;
    d.run_simulation();                                                    
}

to give me derived_class::collect_measurements(), I could either make collect_measurements virtual, or simply overwrite run_simulation() in the derived class, right?
Last edited on Mar 10, 2023 at 8:46pm
Mar 10, 2023 at 9:47pm
Yes, but to "overwrite" a non-virtual method is seldom a good option.
Last edited on Mar 10, 2023 at 9:48pm
Mar 10, 2023 at 10:49pm
Besides, what you have shown could essentially be written:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

class base_class {
};

class derived_class : public base_class {
    void collect_measurements() const {
        std::cout<< "Calling collect_measurements from derived class\n";
    }
public:
    void run_simulation() const {
        collect_measurements();
    }
};


int main () {
   derived_class d;
   d.run_simulation();                                                   
}

If you don't call anything from base class nor require it as interface, then why have the base class at all?
Topic archived. No new replies allowed.