Interface Design in C++
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 61 of 104.
An interface in C++ is an abstract class where all member functions are pure virtual. Unlike abstract classes that may contain some implementation, interfaces define only what operations must exist - they specify a contract without any behavior.
class Drawable {
public:
virtual void draw() = 0;
virtual void resize(double factor) = 0;
virtual ~Drawable() = default;
};This Drawable interface guarantees that any implementing class will have draw() and resize() methods, but says nothing about how they work. This separation is powerful - code can depend on the interface without knowing anything about the concrete types.
class Circle : public Drawable {
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override { std::cout << "Drawing circle" << std::endl; }
void resize(double factor) override { radius *= factor; }
};
class Button : public Drawable {
std::string label;
public:
Button(std::string l) : label(l) {}
void draw() override { std::cout << "Drawing button: " << label << std::endl; }
void resize(double factor) override { /* resize button */ }
};A class can implement multiple interfaces, enabling flexible designs where objects can fulfill different roles:
class Clickable {
public:
virtual void onClick() = 0;
virtual ~Clickable() = default;
};
class IconButton : public Drawable, public Clickable {
public:
void draw() override { std::cout << "Drawing icon" << std::endl; }
void resize(double factor) override { }
void onClick() override { std::cout << "Clicked!" << std::endl; }
};Interfaces promote loose coupling - your code depends on abstractions rather than concrete implementations, making it easier to extend and test.
Challenge
EasyLet's build a device management system that demonstrates how interfaces define contracts for different types of devices. You'll create two separate interfaces that devices can implement, then build concrete device classes that fulfill one or both of these contracts.
You'll organize your code across three files:
Interfaces.h: Define two pure abstract classes (interfaces) that represent different device capabilities:Powerable— any device that can be turned on and off:- A pure virtual
powerOn()method - A pure virtual
powerOff()method - A pure virtual
getPowerStatus()method returning astd::string - A virtual destructor
Connectable— any device that can connect to a network:- A pure virtual
connect(const std::string& network)method - A pure virtual
disconnect()method - A pure virtual
getConnectionInfo()method returning astd::string - A virtual destructor
- A pure virtual
Devices.h: Implement concrete device classes that use these interfaces:Lamp— implements onlyPowerable:- A private
bool isOnmember (starts asfalse) - A private
std::string namemember - A constructor taking the lamp's name
powerOn()setsisOnto true and prints:<name>: Light turned onpowerOff()setsisOnto false and prints:<name>: Light turned offgetPowerStatus()returns"ON"or"OFF"based on state
SmartTV— implements bothPowerableandConnectable:- Private members:
bool isOn(starts false),std::string brand,std::string currentNetwork(starts empty) - A constructor taking the TV's brand
powerOn()setsisOnto true and prints:<brand> TV: Powered onpowerOff()setsisOnto false, clears the network, and prints:<brand> TV: Powered offgetPowerStatus()returns"ON"or"OFF"connect()stores the network name and prints:<brand> TV: Connected to <network>disconnect()clears the network and prints:<brand> TV: DisconnectedgetConnectionInfo()returns"Connected to <network>"if connected, or"Not connected"if empty
- A private
main.cpp: Read three inputs (each on a separate line):- Lamp name
- TV brand
- Network name
Create a
Lampand aSmartTV. Demonstrate how the same interface can be used with different devices:First, work with both devices through the
Powerableinterface. Store pointers to both in an array ofPowerable*, then loop through and callpowerOn()on each, followed by printing their status as:Status: <powerStatus>Print a blank line, then work with the SmartTV through the
Connectableinterface. Create aConnectable*pointer to your SmartTV, callconnect()with the network name, and print:Connection: <connectionInfo>Print another blank line, then power off both devices through the
Powerablearray and show their final status.
For example, with inputs Desk Lamp, Samsung, and HomeWiFi:
Desk Lamp: Light turned on
Status: ON
Samsung TV: Powered on
Status: ON
Samsung TV: Connected to HomeWiFi
Connection: Connected to HomeWiFi
Desk Lamp: Light turned off
Status: OFF
Samsung TV: Powered off
Status: OFFNotice how the SmartTV can be treated as either a Powerable or a Connectable depending on which interface pointer you use. This flexibility is the power of implementing multiple interfaces — your code can work with any device that fulfills the contract it needs, without knowing the concrete type.
Cheat sheet
An interface in C++ is an abstract class where all member functions are pure virtual. Interfaces define only what operations must exist without any implementation:
class Drawable {
public:
virtual void draw() = 0;
virtual void resize(double factor) = 0;
virtual ~Drawable() = default;
};Classes implement interfaces by overriding all pure virtual functions:
class Circle : public Drawable {
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override { std::cout << "Drawing circle" << std::endl; }
void resize(double factor) override { radius *= factor; }
};A class can implement multiple interfaces using multiple inheritance:
class Clickable {
public:
virtual void onClick() = 0;
virtual ~Clickable() = default;
};
class IconButton : public Drawable, public Clickable {
public:
void draw() override { std::cout << "Drawing icon" << std::endl; }
void resize(double factor) override { }
void onClick() override { std::cout << "Clicked!" << std::endl; }
};Interfaces promote loose coupling by allowing code to depend on abstractions rather than concrete implementations.
Try it yourself
#include <iostream>
#include <string>
#include "Devices.h"
using namespace std;
int main() {
// Read inputs
string lampName;
string tvBrand;
string networkName;
getline(cin, lampName);
getline(cin, tvBrand);
getline(cin, networkName);
// TODO: Create a Lamp and a SmartTV object
// TODO: Create an array of Powerable* pointers containing both devices
// TODO: Loop through the array and call powerOn() on each device
// After each powerOn(), print: Status: <powerStatus>
// TODO: Print a blank line
// TODO: Create a Connectable* pointer to the SmartTV
// Call connect() with the network name
// Print: Connection: <connectionInfo>
// TODO: Print a blank line
// TODO: Loop through the Powerable array and call powerOff() on each device
// After each powerOff(), print: Status: <powerStatus>
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container