C++ Interface Design
In this page:
class InterfaceName {
public:
virtual return_type method_name() = 0;
virtual ~InterfaceName() {}
};
class Implementation : public InterfaceName {
public:
return_type method_name() override { }
};
Pure Interfaces
An interface in C++ is conventionally represented by an abstract class made up entirely of pure virtual functions with no data members of its own — it establishes a contract that every derived class implementing it must fulfill.
Example: Pure Interfaces
// Include std::cout and std::cin
#include <iostream>
// Define the type Printable
class Printable {
// Members below can be accessed from outside the class
public:
virtual void print() = 0;
};
// Define the type Document
class Document : public Printable {
// Members below can be accessed from outside the class
public:
void print() override { std::cout << "Printing document" << std::endl; }
};
// Program execution starts in main()
int main() {
Document d;
d.print();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Implementing Interfaces
To implement an interface, a class inherits from the interface class and supplies a concrete override for every one of its pure virtual methods; only once all of them are implemented does the derived class become instantiable.
Example: Implementing Interfaces
// Include std::cout and std::cin
#include <iostream>
// Define the type Drawable
class Drawable {
// Members below can be accessed from outside the class
public:
virtual void draw() = 0;
};
// Define the type Circle
class Circle : public Drawable {
// Members below can be accessed from outside the class
public:
void draw() override { std::cout << "Circle" << std::endl; }
};
// Program execution starts in main()
int main() {
Circle c;
c.draw();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Multiple Interfaces
Unlike languages that limit a class to one base class, C++ allows multiple inheritance, so a single class can implement several distinct interfaces at once, each contributing its own separate contract that the class must satisfy.
Example: Multiple Interfaces
// Include std::cout and std::cin
#include <iostream>
// Define the type Printable
class Printable {
// Members below can be accessed from outside the class
public:
virtual void print() = 0;
};
// Define the type Savable
class Savable {
// Members below can be accessed from outside the class
public:
virtual void save() = 0;
};
class Document : public Printable, public Savable {
public:
void print() override { std::cout << "Printing" << std::endl; }
void save() override { std::cout << "Saving" << std::endl; }
};
// Program execution starts in main()
int main() {
Document d;
d.print();
d.save();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Loose Coupling
Designing against interfaces rather than concrete classes promotes loose coupling: code that only interacts through an interface pointer doesn't need to know which concrete implementation is behind it, so swapping in a different implementation later doesn't ripple through the rest of the program.
Example: Loose Coupling
// Include std::cout and std::cin
#include <iostream>
// Include std::string
#include <string>
// Define the type Logger
class Logger {
// Members below can be accessed from outside the class
public:
virtual void log(std::string msg) = 0;
};
// Define the type ConsoleLogger
class ConsoleLogger : public Logger {
// Members below can be accessed from outside the class
public:
void log(std::string msg) override { std::cout << msg << std::endl; }
};
void process(Logger &logger) {
logger.log("Processing complete");
}
// Program execution starts in main()
int main() {
ConsoleLogger logger;
process(logger);
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Dependency Inversion
This style of design supports the Dependency Inversion principle, which says high-level modules should depend on abstract interfaces rather than directly on low-level concrete classes — making the overall system easier to extend and easier to test in isolation.
Example: Dependency Inversion
// Include std::cout and std::cin
#include <iostream>
// Define the type Notifier
class Notifier {
// Members below can be accessed from outside the class
public:
virtual void notify() = 0;
};
// Define the type EmailNotifier
class EmailNotifier : public Notifier {
// Members below can be accessed from outside the class
public:
void notify() override { std::cout << "Email sent" << std::endl; }
};
class App {
Notifier ¬ifier;
public:
App(Notifier &n) : notifier(n) {}
void run() { notifier.notify(); }
};
// Program execution starts in main()
int main() {
EmailNotifier email;
App app(email);
app.run();
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Trying to create an instance of an interface class made only of pure virtual functions.
- Forgetting to implement all the interface functions in the concrete class.
- Adding data members to an interface, which mixes interface and implementation.
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: