What We'll Cover

1. Object-Oriented Programming

  • Defining Classes & The class Keyword
  • Access Specifiers (public / private) & Encapsulation
  • Creating Objects, Calling Methods, and Using Constructors

2. Advanced C++ Concepts

  • File I/O with fstream
  • Exception Handling with try and catch
  • Organizing Code with Namespaces

3. The STL In-Depth: Vectors

  • Core std::vector Operations (add, access, iterate)
  • Managing Elements (pop_back, clear, size)

4. The STL In-Depth: Maps

  • Understanding Key-Value Pairs with std::map
  • Core std::map Operations (insert, find, iterate)

Object-Oriented Programming in C++

Classes and Objects

Defining a Class

You define a class using the class keyword. It acts as a container for data (attributes) and functions (methods).

Class Definition Example

This code defines a simple Car class with three data attributes.

class Car {
public: // We'll discuss this next
    // Attributes (member variables)
    std::string brand;
    std::string model;
    int year;
};

Access Specifiers

Access specifiers define how the members (attributes and methods) of a class can be accessed.

  • public: Members are accessible from outside the class.
  • private: Members cannot be accessed from outside the class. This is the default if not specified.

Example: Public vs. Private

Good practice is to keep data private and expose functionality through public methods. This is called Encapsulation.

class BankAccount {
private:
    // This data is safe and cannot be
    // changed directly from outside.
    double balance; 
public:
    // This function is public, so it
    // can be called from outside.
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
};

Creating an Object

Creating an object is called instantiation. You can create an object by declaring a variable of the class type, just like any other variable type (e.g., int, string).

Object Creation Example

Here, we create an instance of our Car class and access its public members using the dot operator (.).

// Create an object (instance) of the Car class
Car myCar;

// Access and set its public attributes
myCar.brand = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;

Class Methods

Methods are functions that belong to a class. They define the behavior of an object and can access its attributes.

Defining a Class Method

This code adds a public printInfo method to the Car class, which can access the object's own data.

class Car {
public:
    std::string brand;
    std::string model;
    int year;

    // A public method
    void printInfo() {
        std::cout << brand << " " << model 
                  << " (" << year << ")";
    }
};

Calling a Class Method

Once you have an object, you use the dot operator (.) to call its public methods.

Car myCar;
myCar.brand = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;

// Call the method on the object
myCar.printInfo();

// Expected Output:
// Ford Mustang (1969)

Constructors

A constructor is a special method that is automatically called when an object is created. It's perfect for initializing attributes.

  • It has the same name as the class.
  • It has no return type (not even void).

Constructor Example

This constructor for the Car class takes arguments to initialize its attributes right when the object is created.

class Car {
public:
    std::string brand;
    std::string model;
    int year;

    // The Constructor
    Car(std::string b, std::string m, int y) {
        brand = b;
        model = m;
        year = y;
    }
};

Using the Constructor

Now, you can create and initialize an object in a single, clean line of code.

// Create a Car object using the constructor
Car myCar("Ford", "Mustang", 1969);
Car yourCar("Honda", "Civic", 2022);

// This is much cleaner than setting each
// member variable one by one!

// myCar.brand is already "Ford"
// myCar.model is already "Mustang"
// myCar.year is already 1969
  

Encapsulation: Getters and Setters

Once data is private, we need a controlled, public way to interact with it. This is a core principle of Encapsulation.

  • Getter: A public method used to "get" or read the value of a private attribute. Also known as an accessor.
  • Setter: A public method used to "set" or modify the value of a private attribute. Also known as a mutator.

This approach protects your data from accidental or invalid changes.

Example: Defining Getters and Setters

This class has private data and public methods to access and modify that data safely.

class BankAccount {
private:
    std::string ownerName;
    double balance;
public:
    // Setter for ownerName
    void setOwnerName(std::string name) {
        ownerName = name;
    }
    // Getter for ownerName
    std::string getOwnerName() {
        return ownerName;
    }
    // Setter for balance
    void setBalance(double bal) {
        balance = bal;
    }
    // Getter for balance
    double getBalance() {
        return balance;
    }
};

Example: Using Setters and Getters

Here's how we use the public methods to interact with the object's private data.

int main() {
    BankAccount myAccount;

    // Use setters to modify private data
    myAccount.setOwnerName("John Doe");
    myAccount.setBalance(500.75);

    // Use getters to retrieve private data
    std::cout << "Account Owner: " << myAccount.getOwnerName() << std::endl;
    std::cout << "Balance: $" << myAccount.getBalance() << std::endl;

    // Direct access would cause a compile error:
    // myAccount.balance = 1000000; // ERROR! 'balance' is private
    return 0;
}

The Power of Setters: Validation

A major advantage of setters is adding logic to validate data before it's assigned. This protects the object's state.

class BankAccount {
private:
    double balance;
public:
    void deposit(double amount) {
        if (amount > 0) { // Validation logic
            balance = balance + amount;
        } else {
            std::cout << "Error: Deposit amount must be positive." << std::endl;
        }
    }
    double getBalance() {
        return balance;
    }
};

// myAccount.deposit(100);  // OK
// myAccount.deposit(-50);  // Will print an error message

Practice 1: The Rectangle Class

Your task is to create a Rectangle class with the following specifications:

  • Private Attributes:
    • width (double)
    • height (double)
  • Public Members:
    • A constructor that accepts a width and a height to initialize the object.
    • A method named getArea() that returns the area (width * height).
    • A method named getPerimeter() that returns the perimeter (2 * (width + height)).

Write a main() function to create a Rectangle object and print its area and perimeter to the console.

Practice 2: The Book Class

Create a Book class that demonstrates good encapsulation:

  • Private Attributes:
    • title (std::string)
    • author (std::string)
    • pages (int)
  • Public Members:
    • A constructor that initializes title, author, and pages.
    • Getter methods for all three attributes: getTitle(), getAuthor(), and getPages().
    • A Setter method setPages(int p) that only changes the page count if the new value p is greater than zero.

In main(), create a Book, print its details, try to set an invalid page count, then set a valid one, and print the details again.

File I/O

C++ provides the `fstream` library for working with files. This library includes three classes: `ifstream` for reading from files, `ofstream` for writing to files, and `fstream` for both reading and writing.

Writing to a File

This code shows how to write to a text file.

#include <iostream>
#include <fstream>

int main() {
    std::ofstream MyFile("filename.txt");
    MyFile << "Files can be tricky, but it is fun enough!";
    MyFile.close();
    return 0;
}

Reading from a File

You can read from a file line by line using a `while` loop with `getline()`.

Reading from a File Example

This code reads a text file and prints its contents to the console.

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::string myText;
    std::ifstream MyReadFile("filename.txt");
    while (getline(MyReadFile, myText)) {
        std::cout << myText;
    }
    MyReadFile.close();
    return 0;
}

Exception Handling

Exception handling is a mechanism to handle runtime errors. C++ provides three keywords for this: `try`, `catch`, and `throw`.

Exception Handling Example

This code shows how to use `try` and `catch` to handle a division by zero error.

try {
    int age = 15;
    if (age >= 18) {
        std::cout << "Access granted - you are old enough.";
    } else {
        throw (age);
    }
}
catch (int myNum) {
    std::cout << "Access denied - You must be at least 18 years old.\n";
    std::cout << "Age is: " << myNum;
}

Namespaces

Namespaces provide a solution for preventing name conflicts in large projects. The `using namespace` directive can be used to avoid having to prefix standard library names with `std::`.

Namespace Example

This code shows how to use the `std` namespace for different libraries.

#include 
#include 
#include 

// Import only specific items you need
using std::cout;
using std::endl;
using std::vector;
using std::string;

int main() {
    vector numbers = {1, 2, 3, 4, 5};
    string message = "Hello World";
    
    cout << message << endl;
    
    for (int num : numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    return 0;
}
}

The Standard Template Library (STL)

The STL is a set of C++ template classes to provide common programming data structures and functions such as lists, stacks, arrays, etc. It is a library of container classes, algorithms, and iterators.

STL Containers

Some of the most common STL containers include:

  • `vector`: a dynamic array
  • `list`: a doubly-linked list
  • `map`: a key-value store
  • `set`: a collection of unique items

C++ STL: Vectors

Dynamic Arrays in C++

What is a `std::vector`?

A `std::vector` is a sequence container that encapsulates dynamic size arrays. It is part of the C++ Standard Template Library (STL).

Why Use `std::vector`?

  • Automatic memory management
  • Rich set of member functions
  • Bounds checking with `.at()`
  • More convenient than C-style arrays

Declaring a Vector

To use `std::vector`, you need to include the `` header.

#include <vector>

std::vector<int> myVector;

Adding Elements

You can add elements to the end of a vector using the `push_back()` member function.

`push_back()` Example

This code adds three integers to a vector.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector;
    myVector.push_back(10);
    myVector.push_back(20);
    myVector.push_back(30);
    return 0;
}

Accessing Elements

You can access elements of a vector using the `[]` operator or the `.at()` member function. `.at()` provides bounds checking and will throw an exception if you try to access an out-of-bounds element.

Accessing Elements Example

This code accesses the second element of the vector.

std::vector<int> myVector = {10, 20, 30};
int secondNumber = myVector[1];
int thirdNumber = myVector.at(2);

Getting the Size

You can get the number of elements in a vector using the `.size()` member function.

`.size()` Example

This code prints the size of the vector.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector = {10, 20, 30};
    std::cout << "The size of the vector is: " << myVector.size();
    return 0;
}

Iterating Through a Vector

You can iterate through a vector using a traditional `for` loop, a range-based `for` loop, or iterators.

Range-Based `for` Loop

This is the most common and convenient way to iterate through a vector.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> myVector = {10, 20, 30};
    for (int element : myVector) {
        std::cout << element << " ";
    }
    return 0;
}

Other Useful Member Functions

  • `pop_back()`: removes the last element
  • `clear()`: removes all elements
  • `empty()`: returns `true` if the vector is empty
  • `resize()`: changes the number of elements stored

Vectors of Objects

You can create vectors of any type, including your own custom classes.

Example: Complete Vector Program

This program creates a vector of strings, adds items, and then prints them.

#include <iostream>
#include <vector>
#include <string>

int main() {
    // Vector of strings
    std::vector<std::string> fruits;

    fruits.push_back("Apple");
    fruits.push_back("Banana");
    fruits.push_back("Orange");

    std::cout << "We have " << fruits.size() << " fruits:" << std::endl;

    for (const std::string& fruit : fruits) {
        std::cout << "- " << fruit << std::endl;
    }
    return 0;
}

Example: Modifying a Vector

This example demonstrates adding, removing, and clearing elements.

std::vector<int> numbers = { 1, 2, 3, 4, 5 };
// numbers contains {1, 2, 3, 4, 5}, size is 5

numbers.pop_back(); // Removes the last element (5)
// numbers contains {1, 2, 3, 4}, size is 4

// Access and modify an element
numbers.at(0) = 99;
// numbers contains {99, 2, 3, 4}

if (!numbers.empty()) {
    std::cout << "Vector is not empty." << std::endl;
}

numbers.clear(); // Removes all elements
// numbers is empty, size is 0

if (numbers.empty()) {
    std::cout << "Vector is now empty." << std::endl;
}

Example: Vector of Custom Objects

You can store objects of your own classes in a vector.

class Student {
public:
    std::string name;
    int id;
    Student(std::string n, int i) : name(n), id(i) {}
};

int main() {
    std::vector<Student> classList;

    classList.push_back(Student("Alice", 101));
    classList.push_back(Student("Bob", 102));

    for (const Student& s : classList) {
        std::cout << "ID: " << s.id << ", Name: " << s.name << std::endl;
    }
    return 0;
}

C++ STL: Maps

Key-Value Pairs in C++

What is a `std::map`?

A `std::map` is an associative container that stores elements in a mapped fashion. Each element has a key value and a mapped value. No two mapped values can have the same key values.

Why Use `std::map`?

  • Efficient lookup, insertion, and deletion of elements based on keys.
  • Keys are always sorted.
  • Useful for creating dictionaries, phone books, etc.

Declaring a Map

To use `std::map`, you need to include the `` header.

#include <map>
#include <string>

std::map<std::string, int> myMap;

Inserting Elements

You can insert elements into a map using the `[]` operator or the `insert()` member function.

Insertion Example

This code inserts three key-value pairs into a map.

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> ages;
    ages["Alice"] = 30;
    ages["Bob"] = 25;
    ages.insert(std::make_pair("Charlie", 35));
    return 0;
}

Accessing Elements

You can access the value associated with a key using the `[]` operator or the `.at()` member function. `.at()` provides bounds checking.

Accessing Elements Example

This code accesses the age of Bob.

std::map<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;

int bobsAge = ages["Bob"];

Checking if a Key Exists

You can check if a key exists in a map using the `.count()` or `.find()` member functions.

`.count()` Example

This code checks if the key "David" exists in the map.

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> ages;
    ages["Alice"] = 30;

    if (ages.count("David")) {
        std::cout << "David is in the map.";
    } else {
        std::cout << "David is not in the map.";
    }
    return 0;
}

Iterating Through a Map

You can iterate through a map using a range-based `for` loop or iterators.

Range-Based `for` Loop

This is a convenient way to iterate through a map.

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> ages;
    ages["Alice"] = 30;
    ages["Bob"] = 25;

    for (const auto& pair : ages) {
        std::cout << pair.first << " is " << pair.second << " years old.\n";
    }
    return 0;
}

Other Useful Member Functions

  • `erase()`: removes an element
  • `clear()`: removes all elements
  • `empty()`: returns `true` if the map is empty
  • `size()`: returns the number of elements

When to Use a Map

Use a map when you need to store and retrieve data based on a unique key. It's a great choice for implementing look-up tables, dictionaries, and frequency counters.