-4

I want to practice C++ by coding a simple mobile phone application with an interchangeable system. I created a System base class and also created MyOS class which extends the System class. In the Phone class, I have a variable of System class because I think like in Java, you can assign it with child class. (eg. System sys = new MyOS();). But in C++ it calls the function in the base class.

What I want to work in C++ but it's in Java.

public class MyParent {
    public void start() {
        System.out.println("start() executed in MyParent");
    }
}

public class MyChild extends MyParent {
    @Override
    public void start() {
        System.out.println("start() excecuted in MyChild");
    }
}

public class Inherit {
    MyParent parent;
    
    public Inherit(MyParent parent) {
        this.parent = parent;
    }
    
    public void start() {
        parent.start();
    }
}

public class TestInherit {
    public static void main(String[] args) {
        Inherit i = new Inherit(new MyChild());
        i.start();
    }
}

Output: start() excecuted in MyChild

My current c++ code:

System.h

#pragma once

#include <iostream>

class System {
public:

    void start() {
        std::cout << "Booting System..." << std::endl;
    }
};

MyOS.h

#pragma once

#include <iostream>
#include "System.h"

class MyOS: public System {
public:

    // Override
    void start() {
        std::cout << "Booting MyOS..." << std::endl;
    }
};

Phone.h

#pragma once

#include "System.h"

class Phone {
public:

    Phone(System system) {
        Phone::system = system;
    }

    void start() {
        system.start();
    }

private:

    System system;
};

MyPhone.cpp

#include "MyOS.h"
#include "Phone.h"
#include "System.h"

int main() {
    MyOS os;
    Phone myPhone(os);
    myPhone.start();

    return 0;
}

Output: Booting System...
FOUR BITS
  • 21
  • 4
  • What about making that method `virtual`? – πάντα ῥεῖ Apr 24 '22 at 09:05
  • How did it compile? You should get a compiler error. Which compiler are you using? – Sadaananth Anbucheliyan Apr 24 '22 at 09:07
  • `virtual void start()` in System and `void start() override` in MyOS still outputs `Booting System...` – FOUR BITS Apr 24 '22 at 09:10
  • The reason is System object is not pointer in Phone class. It gets resolved at compile time and system is getting called. Make it a pointer for run time polymorphism – Sadaananth Anbucheliyan Apr 24 '22 at 09:12
  • Read about polymorphism in your favourite C++ book. Don't assume that the differences to Java are just a matter of syntax. – molbdnilo Apr 24 '22 at 09:12
  • Your `Phone` should have a **pointer** to a `System`, not a value – donkopotamus Apr 24 '22 at 09:13
  • @FOURBITS It looks like you need to learn C++ from [good books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), instead of guessing how to write C++ code. The `virtual` keyword is fundamental in getting polymorphism to work in C++. Also, do not use Java as a model in writing C++ code -- Java is not C++. As you can see, every function is "virtual" in Java, but that is not the case with C++. – PaulMcKenzie Apr 24 '22 at 09:13
  • @FOURBITS `Phone(System system)` -- This is also another difference in C++. You are passing `System` by value. You are not passing a reference. That `system` is temporary, and will be gone when the constructor for `Phone` is completed. This is much different than Java, where the `system` is a reference to the `System` that is passed. It's big differences like that which makes it a requirement to *not* use Java as a model in writing C++ code. If you continue to use Java as a model, your program will be buggy, inefficient, or just look weird to an actual C++ programmer. – PaulMcKenzie Apr 24 '22 at 09:21

2 Answers2

0

There are 2 problems with your given code:

Problem 1

The method start is a non-virtual member function so you're not actually overriding it in the MyOS.

So first step is to make it virtual as shown below.

Problem 2

Moreover, in order for runtime binding to work, you must make the call to the virtual-member function using a pointer or a reference to the Base class(which is System) object as shown below:


Solution

Here we make sure that start is a virtual member function and also that the data member system is an lvalue reference to System so that dynamic binding can actually happen.

MyOS.h

#pragma once

#include <iostream>
#include "System.h"

class MyOS: public System {
public:

    // Override
    public: void start() override {    //override keyword added here to make the intention clear
        std::cout << "Booting MyOS..." << std::endl;
    }
};

System.h

#pragma once

#include <iostream>

class System {
public:

    public:
//--vvvvvvv------------------->virtual keyword added here
    virtual void start() {
        std::cout << "Booting System..." << std::endl;
    }
};

Phone.h

#pragma once

#include "System.h"

class Phone {
public:

    Phone(System& psystem):system(psystem){
    }

    void start() {
        system.start();
    }

private:
//--------v----------->lvalue reference to System
    System& system;
};

main.cpp

#include "MyOS.h"
#include "Phone.h"
#include "System.h"

int main() {
    MyOS os;
    Phone myPhone(os);
    myPhone.start();

    return 0;
}

The output of the above program is:

Booting MyOS...

Demo

Note

Note that we can also make the data member system to be a pointer to a System object instead of making it an lvalue reference to a System object. Demo with pointer. Also note in some circumstances, classes used as the root of an inheritance hierarchy should define a virtual destructor. Refer to why virtual destructs are used.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • I think in `System.h` you could make it a pure virtual function even. – F. Müller Apr 24 '22 at 09:21
  • @F.Müller I have discussed one possible solution. – Jason Apr 24 '22 at 09:23
  • You left out that `MyOS` cannot be used in some circumstances safely, unless a virtual destructor is present. – PaulMcKenzie Apr 24 '22 at 09:25
  • @PaulMcKenzie That is completely unnecessary here(in the current situation). I just wanted to explain the basic working. I don't take responsibility for what you can do by modifying the code. For properly learning something a book has to be used. – Jason Apr 24 '22 at 09:29
  • You could link to [why virtual destructors are used](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors). – PaulMcKenzie Apr 24 '22 at 09:35
  • @PaulMcKenzie I have added a note at the end of my answer as per your suggestion. – Jason Apr 24 '22 at 09:42
0

If you're coming from java, you need to remember that every non-primitive variable or field in java is implicitly a pointer, so to make equivalent C++ code, you need to make all interclass references into pointers.

In addition, every method in java is implicitly virtual, so if you want to override them, you need an explicit virtual in C++.

So you end up with something like:

#include <iostream>

class System {
public:

    virtual void start() {
        std::cout << "Booting System..." << std::endl;
    }
};

class MyOS: public System {
public:

    // Override
    void start() override {
        std::cout << "Booting MyOS..." << std::endl;
    }
};

class Phone {
public:

    Phone(System *system) {
        this->system = system;
    }

    void start() {
        system->start();
    }

private:

    System *system;
};

int main() {
    MyOS os;
    Phone myPhone(&os);
    myPhone.start();

    return 0;
}

Of course, using raw pointers is a recipe for memory leaks and corruption, as C++ does not have built in garbage collection. So you generally want to use "smart" pointers instead -- either std::unique_ptr or std::shared_ptr

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226