C++

From Computernewb Wiki
Revision as of 18:16, 20 May 2022 by Undefishin (talk | contribs)
Jump to navigation Jump to search

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);
}