0

I am trying to implement the modular architecture with C++ where I have a .h file for every .cpp file. The files currently only have empty functions:

cycle.h

#include <iostream>
#ifndef CYCLIST_H
#define CYCLIST_H
#pragma once

class cyclist{
private:
    double width;
    double length;
    int helment;
public:
    void Predict();
    double get_width();
    double get_length();
    int get_helmet();
};

#endif

cycle.cpp

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

void cyclist::Predict(){std::cout << "Predict something!" << std::endl;}
double cyclist::get_width(){}
double cyclist::get_length(){}
int cyclist::get_helmet(){}

vehicle.h

#include <iostream>
#ifndef VEHICLE_H
#define VEHICLE_H
#pragma once

class vehicle{
private:
    double width;
    double length;
    int number_of_doors;
public:
    void Predict();
    double get_width();
    double get_length();
    int get_number();
};

#endif

vehicle.cpp

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

void vehicle::Predict(){std::cout << "Predict something!" << std::endl;}
double vehicle::get_width(){}
double vehicle::get_length(){}
int vehicle::get_number(){}

As you can see, I am repeating the get_wdith(), get_height(), and Predict() functions. The reason being that each of them will have a different implementation of the same function.

How can I derive all these functions from a base class? If I am using a base class, would/can I still use .h files?

Edit

So what I created was a single header file Base.h like this:

#include <iostream>
#pragma once

class Base{
    public:
        virtual ~Base() = default;
        virtual void Predict() = 0;
        virtual double get_width() = 0;
        virtual double get_length() = 0;
        virtual int get_number_of_doors() = 0;
        virtual bool get_helmet_state();
};

and then I decided to inherit these from cyclist and vehicle classes like this:

cyclist.cpp

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

void Base::Predict(){std::cout << "Predict something!" << std::endl;}
double Base::get_width(){return 0;}
double Base::get_length(){return 0;}
bool Base::get_helmet_state(){return true;}

vehicle.cpp

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

void Base::Predict(){std::cout << "Predict something!" << std::endl;};
double Base::get_width(){return 0;}
double Base::get_length(){return 0;}
int Base::get_number_of_doors(){return 0;}

Now when I run it, I get an error saying that there are multiple definitions of Base::Predict(). This function will be implemented differently in each class. I tried to add the override keyword but it didn't let me.

Onur-Andros Ozbek
  • 2,998
  • 2
  • 29
  • 78
  • 2
    Quick answer is yes. You could create an abstract base class that only has a `.h` file for it, then for each of the children's `.h` file, you would include that base class's header file. – Ranoiaetep Apr 30 '21 at 00:15
  • Side note, you only need one of the include guards, either `#ifndef ...` or `#pragma once` – Ranoiaetep Apr 30 '21 at 00:16
  • 2
    You are not "*repeating*" those functions since, as you say, "*each of them will have a different implementation*". Deriving from a common base class is certainly possible, but before you do that ask yourself what the expected benefit is, in other words what the problem is that you mean to solve by doing that. – dxiv Apr 30 '21 at 00:18
  • @Ranoiaetep Depends. I see `once` and macro guards in in some portable code where they want the performance advantages that may be in `once` if properly implemented and fully supported by a compiler without suffering the nigh-inscrutable errors that result from only using `once` on a platform that does not or cannot support it. – user4581301 Apr 30 '21 at 00:36
  • 1
    This doesn’t address the question, but none of those headers uses any names from ``, so they should not have `#include `. And especially not outside of the include guards. – Pete Becker Apr 30 '21 at 01:37

1 Answers1

3

Step One: Apply Liskov Substitution Principle to make sure inheritance makes sense.

Step One-A: Make sure Composition isn't a better fit.

Step Two: Make base class

class Base // For demonstration purposes only. Give this a more descriptive name in your code
{ 
public: 
    virtual ~Base() = default;
    virtual void Predict() = 0;
    virtual double get_width() = 0;
    virtual double get_length() = 0;
};

Feel tree to pop this in its own header if you want. Nothing prevents an inheritance hierarchy from using and being divided up into headers.

Depending on your use case, you may or may not need the destructor, but if you're learning C++, make your life easier and put it in. Having it may waste some time at runtime, but you'll have fewer weird problems to have to debug if your inexperience leads you down the wrong path.

All functions you want to override must be declared virtual. virtual is sticky, so you don't have to keep repeating it in the derived classes, but keep an eye out for final.

The = 0 on the end of the virtual functions makes them pure and makes the class abstract. This may or may not be the behaviour you want, but each of them will have a different implementation strongly hints that it is. Read the linked documentation to be sure.

Step Three: Inherit

class cyclist: public Base{
private:
    double width;
    double length;
    int helment;
public:
    void Predict() override;
    double get_width() override;
    double get_length() override;
    int get_helmet();
};

Note that the override keyword is not a hard requirement, but it can save you a lot of trouble by picking off mistakes, costs only a few seconds to type, an insignificant amount of time to compile, and absolutely nothing at runtime. There are a few win-win situations in the world, so take advantage of this one.

Addendum:

If you have behaviour shared between classes in the overridden functions, consider moving the code for the shared behaviour into a protected support function and calling the support function from the overridden functions to eliminate the duplication.

class Base // For demonstration purposes only. Give this a more descriptive name in your code
{ 
protected: 
    double predict_support();
public: 
    virtual ~Base() = default;
    virtual void Predict() = 0;
    virtual double get_width() = 0;
    virtual double get_length() = 0;
};

and later in Base.cpp

double Base::predict_support()
{
    return do_complicated_math();
}

and then use it something like

void cyclist::Predict()
{
    std::cout << "Predict something: " << predict_support() << std::endl;
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • I've never seen a use-case of interfaces or ADTs where the virtual destructor in the base class *wasn't* needed. – Casey Apr 30 '21 at 00:55
  • @Casey I've seen it done in a Meyer's Singleton-like factory class. Destruction was handled at program exit when `static` variables were destroyed. Neat idea, but blew up occasionally because another global variable tried to use the singleton after it was destroyed. Such is the wages of global variables and singletons. – user4581301 Apr 30 '21 at 01:00
  • When I do `class cyclist: public Base{}`, I get a red underline under Base saying that `expected Class name` – Onur-Andros Ozbek Apr 30 '21 at 01:01
  • The `Base` and `cyclist` are not in the same file – Onur-Andros Ozbek Apr 30 '21 at 01:03
  • Ensure that the base class is visible to `cyclist`. Does cyclist.h include the appropriate header containing Base? Have you coded your way into a circular reference by accidentally including cyclist.h in Base.h? – user4581301 Apr 30 '21 at 01:03
  • And make sure this isn't just a code analysis tool pulling your chain by operating on cached data or an unsaved file. Build the project and see what the compiler thinks. – user4581301 Apr 30 '21 at 01:05
  • @user4581301 Check the edit on the post. I made those changes. – Onur-Andros Ozbek Apr 30 '21 at 01:20
  • There are severe problems with what you posted that need to be addressed, but you should ask a new question for help with them. The more you add to a single question, the less useful it becomes to future programmers. The big problem is if you want cycliust to override functions in `Base, instead of `double Base::get_width(){return 0;}` you want `double Cyclist::get_width(){return 0;}` – user4581301 Apr 30 '21 at 06:57