C++
C++ is an object-oriented programming language created by Bjarne Stroustrup in 1985 as a variant of the C programming language. It includes features not found in C such as classes, virtual dispatch inside of said classes, standardized memory allocation and deallocation, exception unwinding, and a standard library of generic utilities known as the STL (which provides containers like strings, and the vector<T> type, alongside lots more).
The current standardized C++ version is C++20, which added the "consteval" keyword and the Concepts TS, among lots of other improvements.
Examples
Hello World
This program prints out "Hello, world!" to the console.
#include <iostream>
int main() { // Main function: must return a variable of int type. This is the program's exit code.
std::cout << "Hello, world!\n";
return 0; // Quit with the exit code of 0
}
Fizz Buzz
This is a more complex example: this program prints out "Fizz" for each multiple of 3, "Buzz" for each multiple of 5, and "Fizz Buzz" for each multiple of both. This program demonstrates functions besides main()
, the if
statement, the ternary operator (condition ? ifTrue : ifFalse
), and loops.
It also shows the use of the new strongly typed "enum class" kind of enums and the switch() statement.
#include <iostream>
enum class FizzBuzzType {
None,
FizzBuzz,
Fizz,
Buzz
};
FizzBuzzType GetFizzBuzzType(int number) {
if(number % 3 == 0 && number % 5 == 0) {
return FizzBuzzType::FizzBuzz;
}
if(number % 3 == 0) {
return FizzBuzzType::Fizz;
}
if(number % 5 == 0) {
return FizzBuzzType::Buzz;
}
return FizzBuzzType::None;
}
int main() {
// i starts as 1, the loop continues until it is above 100, and i increments each loop.
for(int i = 1; i <= 100; i++) {
// This refactored switch statement is better than before.
// It also calculates the value only once.
switch(GetFizzBuzzType(i)) {
case FizzBuzzType::FizzBuzz:
i < 100 ? std::cout << "Fizz Buzz, " : std::cout << "Fizz Buzz" << '\n';
break;
case FizzBuzzType::Fizz:
i < 100 ? std::cout << "Fizz, " : std::cout << "Fizz" << std::endl;
break;
case FizzBuzzType::Buzz:
i < 100 ? std::cout << "Buzz, " : std::cout << "Buzz" << std::endl;
break;
case FizzBuzzType::None:
i < 100 ? std::cout << i << ", " : std::cout << i << '\n';
break;
}
}
return 0;
}
Virtual Functions
Virtual functions are functions part of a class which can be implemented in another inheriting class, allowing the actual implementation to be located somewhere else or even hidden from code.
The example code here shows how a completely abstract class can be implemented inside of a different translation unit (source file), so that the main program only needs the interface to work.
Interface.h:
#include <memory>
// This class is "abstract" meaning it does not implement any of the virtual functions.
// A more sophisticated interface can provide non-virtual functions as well as
// base-class implementations of some virtual functions.
class MyInterface {
public:
// Declare a virtual destructor.
// This makes destructing a MyInterface* not leak memory,
// and also sets this class up for proper RTTI data generation.
virtual ~MyInterface() = default;
// Do something.
virtual void Run() = 0;
};
// Creates a thing which implements the above interface.
// Essentially a stateless factory.
std::shared_ptr<MyInterface> CreateAThing();
Interface.cpp:
#include "Interface.h"
#include <cstdio>
namespace {
// Implements MyInterface.
class MyInterfaceImpl : public MyInterface {
public:
virtual void Run() override {
std::printf("MyInterfaceImpl::Run()\n");
}
};
}
std::shared_ptr<MyInterface> CreateAThing() {
return std::make_shared<MyInterfaceImpl>();
}
main.cpp:
#include "Interface.h"
int main() {
auto interface = CreateAThing();
thing->Run();
return 0;
}
Generic code
C++ supports "templates" which are the front and center way of writing generic code. Alongside this, is now the Concepts TS, allowing us to with ease constrain what types a template can operate on.
#include <concepts>
// This concept constrains to types
// which can perform arithmetic.
template <class T>
concept Arithmetic = requires(T left, T right) {
{ left + right } -> std::same_as<T>;
{ left - right } -> std::same_as<T>;
{ left * right } -> std::same_as<T>;
{ left / right } -> std::same_as<T>;
// This maybe shouldn't also be checking for
// EqualityComparable and LessThanComparable?
{ left < right } -> std::same_as<bool>;
{ left > right } -> std::same_as<bool>;
{ left <= right } -> std::same_as<bool>;
{ left >= right } -> std::same_as<bool>;
{ left == right } -> std::same_as<bool>;
{ left != right } -> std::same_as<bool>;
};
// A (pretty useless) sample of concepts.
// This template is properly constrained to only
// work on arithmetic types, and none else.
// If a user defined type is considered arithmetic,
// this code will automatically work.
template <Arithmetic T, Arithmetic Y>
T Add(T left, Y right) {
return left + right;
}
struct S{};
int main() {
return Add(1, 2);
// Uncomment to see what happens; it won't be 50 lines
// of template errors.
//return Add(S{}, 21);
}