C++ Programming

OOP, STL, Templates & Modern C++ Features

C++ Basics

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    // Output
    cout << "Hello, World!" << endl;
    
    // Variables
    int age = 25;
    double salary = 50000.50;
    string name = "Alice";
    bool isActive = true;
    
    // Input
    cout << "Enter your name: ";
    cin >> name;
    
    // getline for full line
    getline(cin, name);
    
    // Reference variable
    int x = 10;
    int& ref = x;  // ref is alias for x
    ref = 20;      // x is now 20
    
    // Pointers (similar to C)
    int* ptr = &x;
    cout << *ptr << endl;  // 20
    
    // Constants
    const int MAX = 100;
    constexpr int SIZE = 50;  // Compile-time constant
    
    // Auto type deduction
    auto num = 42;        // int
    auto pi = 3.14;       // double
    auto text = "Hello";  // const char*
    
    return 0;
}

Object-Oriented Programming

C++
// Class definition
class Person {
private:        // Access modifier
    string name;
    int age;
    
protected:
    string id;
    
public:
    // Constructor
    Person(string n, int a) : name(n), age(a) {
        cout << "Constructor called" << endl;
    }
    
    // Default constructor
    Person() : name("Unknown"), age(0) {}
    
    // Copy constructor
    Person(const Person& other) {
        name = other.name;
        age = other.age;
    }
    
    // Destructor
    ~Person() {
        cout << "Destructor called" << endl;
    }
    
    // Getters
    string getName() const { return name; }
    int getAge() const { return age; }
    
    // Setters
    void setName(string n) { name = n; }
    void setAge(int a) {
        if (a >= 0) age = a;
    }
    
    // Method
    void display() const {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
    
    // Static member
    static int count;
    static int getCount() { return count; }
};

int Person::count = 0;  // Initialize static member

int main() {
    Person p1("Alice", 25);
    Person p2 = p1;  // Copy constructor
    
    p1.display();
    p2.setAge(30);
    
    Person* ptr = new Person("Bob", 30);
    ptr->display();
    delete ptr;  // Don't forget to free!
    
    return 0;
}

Inheritance

C++
// Base class
class Animal {
protected:
    string name;
public:
    Animal(string n) : name(n) {}
    void eat() { cout << name << " is eating" << endl; }
};

// Single inheritance
class Dog : public Animal {
public:
    Dog(string n) : Animal(n) {}
    void bark() { cout << name << " says Woof!" << endl; }
};

// Multiple inheritance
class Swimmer {
public:
    void swim() { cout << "Swimming..." << endl; }
};

class Duck : public Animal, public Swimmer {
public:
    Duck(string n) : Animal(n) {}
    void quack() { cout << name << " says Quack!" << endl; }
};

// Multilevel inheritance
class Puppy : public Dog {
public:
    Puppy(string n) : Dog(n) {}
    void play() { cout << name << " is playing" << endl; }
};

// Access specifiers in inheritance:
// public    : base public->public, protected->protected
// protected : base public->protected, protected->protected  
// private   : base public->private, protected->private

int main() {
    Dog dog("Buddy");
    dog.eat();    // Inherited
    dog.bark();   // Own method
    
    Duck duck("Donald");
    duck.eat();
    duck.swim();
    duck.quack();
    
    return 0;
}

Polymorphism

C++
// Function overloading (Compile-time)
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }

// Operator overloading
class Complex {
    double real, imag;
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // Overload + operator
    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imag + other.imag);
    }
    
    // Overload << for output
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << c.real << " + " << c.imag << "i";
        return os;
    }
};

// Virtual functions (Runtime polymorphism)
class Shape {
public:
    virtual double area() = 0;  // Pure virtual (abstract)
    virtual void draw() {
        cout << "Drawing shape" << endl;
    }
    virtual ~Shape() {}  // Virtual destructor
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override {  // Override keyword (C++11)
        return 3.14159 * radius * radius;
    }
    
    void draw() override {
        cout << "Drawing circle" << endl;
    }
};

class Rectangle : public Shape {
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() override {
        return width * height;
    }
};

int main() {
    // Function overloading
    cout << add(1, 2) << endl;       // 3
    cout << add(1.5, 2.5) << endl;   // 4.0
    
    // Operator overloading
    Complex c1(1, 2), c2(3, 4);
    cout << c1 + c2 << endl;  // 4 + 6i
    
    // Polymorphism with pointers
    Shape* shapes[2];
    shapes[0] = new Circle(5);
    shapes[1] = new Rectangle(4, 3);
    
    for (int i = 0; i < 2; i++) {
        shapes[i]->draw();
        cout << "Area: " << shapes[i]->area() << endl;
        delete shapes[i];
    }
    
    return 0;
}

Standard Template Library (STL)

C++
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>

// Vector - Dynamic array
vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);
vec.pop_back();
vec.insert(vec.begin() + 2, 10);
vec.erase(vec.begin());
int size = vec.size();
bool empty = vec.empty();

// Iterate
for (int x : vec) cout << x << " ";
for (auto it = vec.begin(); it != vec.end(); it++) cout << *it;

// Map - Key-value pairs
map<string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
ages.insert({"Charlie", 35});

if (ages.find("Alice") != ages.end()) {
    cout << "Found: " << ages["Alice"] << endl;
}

for (auto& [key, value] : ages) {  // C++17
    cout << key << ": " << value << endl;
}

// Set - Unique sorted elements
set<int> nums = {3, 1, 4, 1, 5};  // {1, 3, 4, 5}
nums.insert(2);
nums.erase(3);

// Stack - LIFO
stack<int> s;
s.push(1);
s.push(2);
int top = s.top();  // 2
s.pop();

// Queue - FIFO
queue<int> q;
q.push(1);
q.push(2);
int front = q.front();  // 1
q.pop();

// Priority Queue - Max heap by default
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(2);
int max = pq.top();  // 3

// Min heap
priority_queue<int, vector<int>, greater<int>> minPq;

// Algorithms
vector<int> v = {5, 2, 8, 1, 9};
sort(v.begin(), v.end());                    // Ascending
sort(v.begin(), v.end(), greater<int>());    // Descending
reverse(v.begin(), v.end());
int maxVal = *max_element(v.begin(), v.end());
int minVal = *min_element(v.begin(), v.end());
bool found = binary_search(v.begin(), v.end(), 5);

Modern C++ (11/14/17)

C++
#include <memory>

// Lambda expressions
auto add = [](int a, int b) { return a + b; };
cout << add(3, 5) << endl;

// Lambda with capture
int multiplier = 5;
auto multiply = [multiplier](int x) { return x * multiplier; };
auto multiplyRef = [&multiplier](int x) { return x * multiplier; };

// Range-based for loop
vector<int> nums = {1, 2, 3, 4, 5};
for (int n : nums) cout << n;
for (const auto& n : nums) cout << n;

// Smart pointers
// unique_ptr - Exclusive ownership
unique_ptr<int> uptr = make_unique<int>(42);
cout << *uptr << endl;
// uptr automatically deleted when out of scope

// shared_ptr - Shared ownership
shared_ptr<int> sptr1 = make_shared<int>(100);
shared_ptr<int> sptr2 = sptr1;  // Reference count = 2
cout << sptr1.use_count() << endl;

// weak_ptr - Non-owning reference
weak_ptr<int> wptr = sptr1;

// nullptr (instead of NULL)
int* ptr = nullptr;

// Move semantics
string str1 = "Hello";
string str2 = move(str1);  // str1 is now empty

// Structured bindings (C++17)
pair<int, string> p = {1, "one"};
auto [num, text] = p;

// Optional (C++17)
#include <optional>
optional<int> maybeInt = 42;
if (maybeInt.has_value()) {
    cout << maybeInt.value() << endl;
}

// String_view (C++17)
#include <string_view>
void print(string_view sv) {  // No copy
    cout << sv << endl;
}
Modern C++ Best Practices:
• Use smart pointers instead of raw pointers
• Prefer auto for type deduction
• Use range-based for loops
• Use nullptr instead of NULL
• Use constexpr for compile-time constants