Overview
This lab builds two related C++ classes, Vehicle and Car, that show how inheritance lets one class reuse and specialise the behaviour of another instead of duplicating it. Starting from a Vehicle class with its own private data and accessors, you will derive a Car class that reuses Vehicle's constructor and replaces its start() message, then construct a Car in main and observe which version of start() actually runs.
Learning Objectives
By the end of this lab you will be able to:
- Design a class with private state and public getter and setter accessors
- Derive one class from another with class Derived : public Base
- Forward constructor arguments to a base class through the member initializer list
- Explain why re-declaring a non-virtual base method hides it rather than overriding it
Concept
Suppose a game needs several kinds of vehicle, a Car, a Truck, a Motorbike, each with wheels, doors, an engine, a colour, a make and a model. Writing a full class for each one means copying every private field and every getter and setter into each class, and fixing a bug in one copy does nothing for the others. C++ solves this with inheritance. one class, the base class, defines the shared data and behaviour once, and other classes, derived classes, reuse it automatically and add only what makes them different.
A class derives from another with class Car : public Vehicle. This gives Car every member Vehicle has, without retyping any of them. Vehicle's private fields stay inaccessible to Car directly, so Car still goes through Vehicle's public getters and setters, the same as any other code would. Car's constructor still needs to fill in those private fields, so instead of setting them itself it forwards its arguments to Vehicle's constructor through a member initializer list, Car(...) : Vehicle(...) {}, which runs before Car's own constructor body.
A derived class can also declare a member with the same name as one in its base, such as start(). Unless that member is marked virtual in the base, this does not override it. It hides it. which version runs depends on the declared type used to call it, not on what kind of object it actually is.
Tasks
Task 1 - The Vehicle base class
Open Vehicle.h and complete the Vehicle class. It stores everything a vehicle needs as private state, and exposes it only through public methods.
- Declare six private members:
wheelsanddoorsasint, andengine,colour,make,modelasstd::string. - In the constructor, assign each of the six parameters to the matching member using
this->. - Implement
start()to printThe vehicle has started. - Implement the six getters to return their matching member.
- Implement the six setters to assign their parameter to the matching member, with two exceptions.
setWheelsshould silently do nothing if given a negative value, andsetColourshould print a line to the console before assigning if the given colour is"RED".
Success condition: constructing a Vehicle directly and calling start() prints The vehicle has started, and the getters return whatever was last set.
#pragma once
#include <string>
#include <iostream>
class Vehicle
{
private:
// TODO: declare six private members: wheels (int), doors (int),
// engine, colour, make, model (all std::string)
public:
Vehicle(int wheels, int doors, std::string engine, std::string colour, std::string make, std::string model)
{
// TODO: assign each parameter to the matching private member using this->
}
~Vehicle() {}
void start()
{
// TODO: print "The vehicle has started"
}
int getWheels()
{
// TODO: return wheels
}
int getDoors()
{
// TODO: return doors
}
std::string getEngine()
{
// TODO: return engine
}
std::string getColour()
{
// TODO: return colour
}
std::string getMake()
{
// TODO: return make
}
std::string getModel()
{
// TODO: return model
}
void setWheels(int wheels)
{
// TODO: ignore negative values, otherwise assign to the wheels member
}
void setDoors(int doors)
{
// TODO: assign to the doors member
}
void setEngine(std::string engine)
{
// TODO: assign to the engine member
}
void setColour(std::string colour)
{
// TODO: if colour is "RED", print a warning line first, then assign
// it to the colour member either way
}
void setMake(std::string make)
{
// TODO: assign to the make member
}
void setModel(std::string model)
{
// TODO: assign to the model member
}
};
Task 2 - Deriving Car from Vehicle
Open Car.h and complete the Car class so it reuses Vehicle instead of duplicating it.
- Change the class declaration so
Carderives publicly fromVehicle. - Give
Cara constructor with the same six parameters asVehicle's constructor, and forward all six straight through toVehicle's constructor using a member initializer list. - Implement
Car's ownstart()to printThe car has started.
#pragma once
#include "Vehicle.h"
#include <iostream>
class Car // TODO: derive publicly from Vehicle
{
public:
// TODO: write a constructor taking the same six parameters as Vehicle's
// constructor, and forward them to Vehicle's constructor using a member
// initializer list
~Car() {}
void start()
{
// TODO: print "The car has started"
}
};
main.cpp below already constructs a Car and exercises it, so you can test your class as soon as it compiles.
#include <iostream>
#include <string>
#include "Car.h"
int main()
{
Car* myCar = new Car(4, 2, "1.0L", "blue", "Peugeot", "106");
myCar->start();
std::cout << myCar->getMake() << " " << myCar->getModel() << std::endl;
delete myCar;
}
Success condition: running the program prints The car has started, not The vehicle has started, followed by the make and model from the inherited getters. This works even though Vehicle::start is not marked virtual, because Car::start is a separate function that hides it rather than overriding it.
Solution
Try the tasks above before revealing the solution.