Complete C++ Programming Course

By Ramgopal Prajapati | DeviX Solutions

Introduction to C++ Programming

C++ is a general-purpose programming language created by Bjarne Stroustrup as an extension of the C programming language. It has imperative, object-oriented and generic programming features, while also providing facilities for low-level memory manipulation.

C++ is one of the most popular programming languages and is used in systems software, application software, device drivers, embedded software, high-performance server and client applications, and entertainment software such as video games.

Key Features of C++:

  • Object-Oriented Programming
  • Platform Independent
  • Simple and Portable
  • Mid-level programming language
  • Rich Library Support
  • Memory Management
  • Fast Performance
// Your first C++ program
#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

Basic Structure of a C++ Program

Every C++ program consists of one or more functions. The main() function is the entry point of every C++ program. C++ programs are typically organized with header files and source files.

Basic Components:

  1. Preprocessor directives (#include)
  2. Using directives (namespace)
  3. Function declarations
  4. Variables and data types
  5. Statements & Expressions
  6. Comments
// Basic structure of a C++ program
#include <iostream>  // Preprocessor directive
using namespace std; // Using directive

// Global variable declaration
int global_var = 10;

// Function declaration
void custom_function();

// Main function - program entry point
int main() {
    // Local variable declaration
    int local_var = 20;
    
    // Statement
    cout << "Global: " << global_var << ", Local: " << local_var << endl;
    
    // Function call
    custom_function();
    
    return 0;  // Return statement
}

// Function definition
void custom_function() {
    cout << "This is a custom function." << endl;
}

Object-Oriented Programming Concepts

Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around data, or objects, rather than functions and logic. C++ supports several OOP concepts.

Core OOP Concepts:

Concept Description
Class A blueprint for creating objects
Object An instance of a class
Encapsulation Binding data and functions together
Inheritance Creating new classes from existing ones
Polymorphism Ability to take more than one form
Abstraction Hiding implementation details
// Basic class example
#include <iostream>
#include <string>
using namespace std;

// Class definition
class Car {
private:
    string brand;
    string model;
    int year;
    
public:
    // Constructor
    Car(string b, string m, int y) {
        brand = b;
        model = m;
        year = y;
    }
    
    // Method to display car information
    void displayInfo() {
        cout << "Brand: " << brand << endl;
        cout << "Model: " << model << endl;
        cout << "Year: " << year << endl;
    }
    
    // Setter method
    void setYear(int y) {
        year = y;
    }
    
    // Getter method
    int getYear() {
        return year;
    }
};

int main() {
    // Create an object of Car
    Car myCar("Toyota", "Corolla", 2020);
    
    // Call method
    myCar.displayInfo();
    
    // Use setter and getter
    myCar.setYear(2022);
    cout << "Updated Year: " << myCar.getYear() << endl;
    
    return 0;
}

Real-world Application:

OOP concepts are used to model real-world entities and relationships. For example, in a banking system, you might have classes for Account, Customer, Transaction, etc., with methods that represent actions like deposit(), withdraw(), and transfer().

Classes and Objects in C++

A class is a user-defined data type that we can use in our program, and it works as an object constructor. An object is an instance of a class.

Class Components:

  • Access specifiers (public, private, protected)
  • Data members (attributes)
  • Member functions (methods)
  • Constructors and destructors
  • Static members
  • Friend functions and classes
// Classes and objects example
#include <iostream>
#include <string>
using namespace std;

class Rectangle {
private:
    double length;
    double width;
    
public:
    // Default constructor
    Rectangle() {
        length = 1.0;
        width = 1.0;
    }
    
    // Parameterized constructor
    Rectangle(double l, double w) {
        length = l;
        width = w;
    }
    
    // Copy constructor
    Rectangle(const Rectangle &other) {
        length = other.length;
        width = other.width;
    }
    
    // Member function to calculate area
    double area() {
        return length * width;
    }
    
    // Member function to calculate perimeter
    double perimeter() {
        return 2 * (length + width);
    }
    
    // Setter methods
    void setLength(double l) {
        if (l > 0) length = l;
    }
    
    void setWidth(double w) {
        if (w > 0) width = w;
    }
    
    // Getter methods
    double getLength() {
        return length;
    }
    
    double getWidth() {
        return width;
    }
    
    // Static member function
    static void displayInfo() {
        cout << "This is a Rectangle class" << endl;
    }
};

int main() {
    // Create objects using different constructors
    Rectangle rect1; // Default constructor
    Rectangle rect2(5.0, 3.0); // Parameterized constructor
    Rectangle rect3 = rect2; // Copy constructor
    
    // Calculate and display area
    cout << "Area of rect1: " << rect1.area() << endl;
    cout << "Area of rect2: " << rect2.area() << endl;
    cout << "Area of rect3: " << rect3.area() << endl;
    
    // Use setter methods
    rect1.setLength(4.0);
    rect1.setWidth(2.5);
    
    cout << "Updated area of rect1: " << rect1.area() << endl;
    
    // Call static member function
    Rectangle::displayInfo();
    
    return 0;
}

Real-world Application:

Classes and objects are used to model real-world entities. For example, in a university system, you might have classes for Student, Professor, Course, etc., each with their own attributes and methods that define their behavior.

Constructors and Destructors

Constructors and destructors are special member functions of a class that are executed automatically when an object of the class is created or destroyed.

Types of Constructors:

  • Default Constructor
  • Parameterized Constructor
  • Copy Constructor
  • Move Constructor (C++11)
// Constructors and destructors example
#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string name;
    int age;
    int *grades;
    int numGrades;
    
public:
    // Default constructor
    Student() {
        name = "Unknown";
        age = 0;
        grades = nullptr;
        numGrades = 0;
        cout << "Default constructor called" << endl;
    }
    
    // Parameterized constructor
    Student(string n, int a) {
        name = n;
        age = a;
        grades = nullptr;
        numGrades = 0;
        cout << "Parameterized constructor called" << endl;
    }
    
    // Copy constructor
    Student(const Student &other) {
        name = other.name;
        age = other.age;
        numGrades = other.numGrades;
        
        // Deep copy for dynamically allocated memory
        if (numGrades > 0) {
            grades = new int[numGrades];
            for (int i = 0; i < numGrades; i++) {
                grades[i] = other.grades[i];
            }
        } else {
            grades = nullptr;
        }
        
        cout << "Copy constructor called" << endl;
    }
    
    // Destructor
    ~Student() {
        delete[] grades;
        cout << "Destructor called for " << name << endl;
    }
    
    // Method to add grades
    void addGrade(int grade) {
        int *temp = new int[numGrades + 1];
        
        // Copy existing grades
        for (int i = 0; i < numGrades; i++) {
            temp[i] = grades[i];
        }
        
        // Add new grade
        temp[numGrades] = grade;
        numGrades++;
        
        // Delete old array and point to new one
        delete[] grades;
        grades = temp;
    }
    
    // Method to display student information
    void display() {
        cout << "Name: " << name << endl;
        cout << "Age: " << age << endl;
        cout << "Grades: ";
        
        for (int i = 0; i < numGrades; i++) {
            cout << grades[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    // Create objects using different constructors
    Student student1; // Default constructor
    Student student2("Alice", 20); // Parameterized constructor
    
    // Add grades to student2
    student2.addGrade(85);
    student2.addGrade(90);
    student2.addGrade(78);
    
    // Use copy constructor
    Student student3 = student2;
    
    // Display student information
    cout << "\nStudent 2:" << endl;
    student2.display();
    
    cout << "\nStudent 3 (copy of student2):" << endl;
    student3.display();
    
    cout << "\nEnd of main function - destructors will be called automatically" << endl;
    
    return 0;
}

Real-world Application:

Constructors are used to initialize objects with valid states, while destructors are crucial for resource management. For example, in a database application, a constructor might establish a connection to the database, while the destructor would close that connection and free up resources.

Inheritance in C++

Inheritance is a feature of Object-Oriented Programming that allows a class to inherit properties and characteristics from another class. The class that inherits is called the derived class, and the class being inherited from is called the base class.

Types of Inheritance:

  • Single Inheritance
  • Multiple Inheritance
  • Multilevel Inheritance
  • Hierarchical Inheritance
  • Hybrid Inheritance
// Inheritance example
#include <iostream>
#include <string>
using namespace std;

// Base class
class Vehicle {
protected:
    string brand;
    string model;
    int year;
    
public:
    Vehicle(string b, string m, int y) : brand(b), model(m), year(y) {}
    
    void displayInfo() {
        cout << "Brand: " << brand << endl;
        cout << "Model: " << model << endl;
        cout << "Year: " << year << endl;
    }
};

// Derived class - Single inheritance
class Car : public Vehicle {
private:
    int doors;
    string fuelType;
    
public:
    Car(string b, string m, int y, int d, string f) 
        : Vehicle(b, m, y), doors(d), fuelType(f) {}
    
    void displayCarInfo() {
        displayInfo(); // Call base class method
        cout << "Doors: " << doors << endl;
        cout << "Fuel Type: " << fuelType << endl;
    }
};

// Another derived class - Multilevel inheritance
class SportsCar : public Car {
private:
    int topSpeed;
    bool convertible;
    
public:
    SportsCar(string b, string m, int y, int d, string f, int ts, bool conv) 
        : Car(b, m, y, d, f), topSpeed(ts), convertible(conv) {}
    
    void displaySportsCarInfo() {
        displayCarInfo(); // Call base class method
        cout << "Top Speed: " << topSpeed << " mph" << endl;
        cout << "Convertible: " << (convertible ? "Yes" : "No") << endl;
    }
};

// Another base class for multiple inheritance
class Electric {
protected:
    int batteryCapacity;
    int range;
    
public:
    Electric(int bc, int r) : batteryCapacity(bc), range(r) {}
    
    void displayElectricInfo() {
        cout << "Battery Capacity: " << batteryCapacity << " kWh" << endl;
        cout << "Range: " << range << " miles" << endl;
    }
};

// Multiple inheritance
class ElectricCar : public Car, public Electric {
public:
    ElectricCar(string b, string m, int y, int d, string f, int bc, int r) 
        : Car(b, m, y, d, f), Electric(bc, r) {}
    
    void displayElectricCarInfo() {
        displayCarInfo();
        displayElectricInfo();
    }
};

int main() {
    // Single inheritance example
    cout << "=== Car (Single Inheritance) ===" << endl;
    Car myCar("Toyota", "Camry", 2020, 4, "Gasoline");
    myCar.displayCarInfo();
    
    cout << "\n=== Sports Car (Multilevel Inheritance) ===" << endl;
    SportsCar sportsCar("Porsche", "911", 2021, 2, "Gasoline", 190, true);
    sportsCar.displaySportsCarInfo();
    
    cout << "\n=== Electric Car (Multiple Inheritance) ===" << endl;
    ElectricCar electricCar("Tesla", "Model 3", 2022, 4, "Electric", 75, 315);
    electricCar.displayElectricCarInfo();
    
    return 0;
}

Real-world Application:

Inheritance is used to create hierarchical relationships between classes. For example, in a graphics application, you might have a base Shape class with derived classes like Circle, Rectangle, and Triangle, each inheriting common properties but implementing their own specific behaviors.

Polymorphism in C++

Polymorphism means "many forms". In C++, polymorphism allows us to perform a single action in different ways. There are two types of polymorphism: compile-time (function overloading, operator overloading) and runtime (virtual functions).

Types of Polymorphism:

  • Compile-time Polymorphism
    • Function Overloading
    • Operator Overloading
  • Runtime Polymorphism
    • Virtual Functions
    • Function Overriding
// Polymorphism example
#include <iostream>
#include <string>
using namespace std;

// Base class with virtual function
class Shape {
protected:
    string name;
    
public:
    Shape(string n) : name(n) {}
    
    // Virtual function for runtime polymorphism
    virtual void draw() {
        cout << "Drawing a " << name << endl;
    }
    
    // Pure virtual function (makes Shape an abstract class)
    virtual double area() = 0;
    
    // Virtual destructor
    virtual ~Shape() {
        cout << "Shape destructor: " << name << endl;
    }
};

// Derived class
class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(string n, double r) : Shape(n), radius(r) {}
    
    // Override draw function
    void draw() override {
        cout << "Drawing a circle with radius " << radius << endl;
    }
    
    // Implement area function
    double area() override {
        return 3.14159 * radius * radius;
    }
};

// Another derived class
class Rectangle : public Shape {
private:
    double width;
    double height;
    
public:
    Rectangle(string n, double w, double h) : Shape(n), width(w), height(h) {}
    
    // Override draw function
    void draw() override {
        cout << "Drawing a rectangle with width " << width << " and height " << height << endl;
    }
    
    // Implement area function
    double area() override {
        return width * height;
    }
};

// Function overloading (compile-time polymorphism)
void print(int i) {
    cout << "Printing integer: " << i << endl;
}

void print(double d) {
    cout << "Printing double: " << d << endl;
}

void print(const string& s) {
    cout << "Printing string: " << s << endl;
}

// Operator overloading
class Vector {
public:
    double x, y;
    
    Vector(double x = 0, double y = 0) : x(x), y(y) {}
    
    // Overload + operator
    Vector operator+(const Vector& other) {
        return Vector(x + other.x, y + other.y);
    }
    
    // Overload - operator
    Vector operator-(const Vector& other) {
        return Vector(x - other.x, y - other.y);
    }
    
    // Overload << operator for output
    friend ostream& operator<<(ostream& os, const Vector& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }
};

int main() {
    // Runtime polymorphism with virtual functions
    cout << "=== Runtime Polymorphism ===" << endl;
    
    Shape* shapes[2];
    shapes[0] = new Circle("Circle", 5.0);
    shapes[1] = new Rectangle("Rectangle", 4.0, 6.0);
    
    for (int i = 0; i < 2; i++) {
        shapes[i]->draw(); // Calls the appropriate derived class function
        cout << "Area: " << shapes[i]->area() << endl;
        delete shapes[i];
    }
    
    // Compile-time polymorphism: function overloading
    cout << "\n=== Function Overloading ===" << endl;
    print(10);
    print(3.14);
    print("Hello, World!");
    
    // Compile-time polymorphism: operator overloading
    cout << "\n=== Operator Overloading ===" << endl;
    Vector v1(1.0, 2.0);
    Vector v2(3.0, 4.0);
    
    Vector v3 = v1 + v2;
    Vector v4 = v2 - v1;
    
    cout << "v1: " << v1 << endl;
    cout << "v2: " << v2 << endl;
    cout << "v1 + v2: " << v3 << endl;
    cout << "v2 - v1: " << v4 << endl;
    
    return 0;
}

Real-world Application:

Polymorphism is used extensively in GUI frameworks, game development, and any system that needs to handle different types of objects through a common interface. For example, in a drawing application, all shapes can be drawn, moved, or resized through a common interface, even though each shape implements these operations differently.

Templates in C++

Templates are a powerful feature in C++ that allows you to write generic programs. They are the foundation of generic programming, which involves writing code in a way that is independent of any particular type.

Types of Templates:

  • Function Templates
  • Class Templates
  • Template Specialization
  • Variable Templates (C++14)
// Templates example
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// Function template
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

// Function template with multiple parameters
template <typename T1, typename T2>
void printPair(T1 first, T2 second) {
    cout << "First: " << first << ", Second: " << second << endl;
}

// Class template
template <typename T>
class Box {
private:
    T content;
    
public:
    Box(T value) : content(value) {}
    
    T getContent() {
        return content;
    }
    
    void setContent(T value) {
        content = value;
    }
    
    void display() {
        cout << "Box contains: " << content << endl;
    }
};

// Template specialization for const char*
template<>
class Box<const char*> {
private:
    const char* content;
    
public:
    Box(const char* value) : content(value) {}
    
    const char* getContent() {
        return content;
    }
    
    void setContent(const char* value) {
        content = value;
    }
    
    void display() {
        cout << "Box contains string: \"" << content << "\"" << endl;
    }
};

// Class template with non-type parameters
template <typename T, int size>
class Array {
private:
    T arr[size];
    
public:
    void set(int index, T value) {
        if (index >= 0 && index < size) {
            arr[index] = value;
        }
    }
    
    T get(int index) {
        if (index >= 0 && index < size) {
            return arr[index];
        }
        return T(); // Return default value
    }
    
    void display() {
        cout << "Array: [";
        for (int i = 0; i < size; i++) {
            cout << arr[i];
            if (i < size - 1) cout << ", ";
        }
        cout << "]" << endl;
    }
};

int main() {
    // Function template examples
    cout << "=== Function Templates ===" << endl;
    cout << "max(5, 10) = " << max(5, 10) << endl;
    cout << "max(3.14, 2.71) = " << max(3.14, 2.71) << endl;
    cout << "max('a', 'z') = " << max('a', 'z') << endl;
    
    printPair(10, "Hello");
    printPair(3.14, true);
    
    // Class template examples
    cout << "\n=== Class Templates ===" << endl;
    Box<int> intBox(42);
    intBox.display();
    
    Box<string> stringBox("Template Example");
    stringBox.display();
    
    // Template specialization example
    Box<const char*> charBox("Specialized Template");
    charBox.display();
    
    // Non-type template parameter example
    cout << "\n=== Non-type Template Parameters ===" << endl;
    Array<int, 5> intArray;
    
    for (int i = 0; i < 5; i++) {
        intArray.set(i, i * 10);
    }
    
    intArray.display();
    
    Array<string, 3> stringArray;
    stringArray.set(0, "Hello");
    stringArray.set(1, "World");
    stringArray.set(2, "!");
    
    stringArray.display();
    
    return 0;
}

Real-world Application:

Templates are used extensively in the Standard Template Library (STL) to create generic containers and algorithms. They're also used in mathematical libraries for operations on different numeric types, and in any code that needs to work with multiple data types without duplication.

Standard Template Library (STL)

The Standard Template Library (STL) is a powerful set of C++ template classes to provide general-purpose classes and functions with templates that implement many popular and commonly used algorithms and data structures.

Main Components of STL:

  • Containers - store data (vector, list, map, set, etc.)
  • Algorithms - perform operations on data (sort, search, etc.)
  • Iterators - access elements in containers
  • Function Objects (Functors)
// STL example
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <algorithm>
#include <string>
using namespace std;

int main() {
    // Vector example
    cout << "=== Vector ===" << endl;
    vector<int> numbers = {5, 2, 8, 1, 9};
    
    cout << "Original vector: ";
    for (int num : numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // Sort the vector
    sort(numbers.begin(), numbers.end());
    
    cout << "Sorted vector: ";
    for (int num : numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // List example
    cout << "\n=== List ===" << endl;
    list<string> names = {"Alice", "Bob", "Charlie"};
    
    // Add elements to list
    names.push_front("Zoe");
    names.push_back("David");
    
    cout << "List contents: ";
    for (const string& name : names) {
        cout << name << " ";
    }
    cout << endl;
    
    // Map example
    cout << "\n=== Map ===" << endl;
    map<string, int> ageMap;
    
    // Insert key-value pairs
    ageMap["Alice"] = 25;
    ageMap["Bob"] = 30;
    ageMap["Charlie"] = 35;
    
    cout << "Map contents:" << endl;
    for (const auto& pair : ageMap) {
        cout << pair.first << ": " << pair.second << endl;
    }
    
    // Set example
    cout << "\n=== Set ===" << endl;
    set<int> uniqueNumbers = {5, 2, 8, 2, 5, 9, 1};
    
    cout << "Set contents (unique, sorted): ";
    for (int num : uniqueNumbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // Algorithm examples
    cout << "\n=== Algorithms ===" << endl;
    vector<int> values = {5, 2, 8, 1, 9, 3, 6, 4, 7};
    
    // Find an element
    auto it = find(values.begin(), values.end(), 6);
    if (it != values.end()) {
        cout << "Found 6 at position: " << distance(values.begin(), it) << endl;
    }
    
    // Count elements greater than 5
    int count = count_if(values.begin(), values.end(), [](int x) { return x > 5; });
    cout << "Count of numbers greater than 5: " << count << endl;
    
    // Transform elements (square each element)
    vector<int> squaredValues;
    squaredValues.resize(values.size());
    transform(values.begin(), values.end(), squaredValues.begin(), [](int x) { return x * x; });
    
    cout << "Squared values: ";
    for (int num : squaredValues) {
        cout << num << " ";
    }
    cout << endl;
    
    return 0;
}

Real-world Application:

The STL is used in virtually all C++ programs for efficient data storage and manipulation. For example, vectors are used for dynamic arrays, maps for key-value storage (like dictionaries), and algorithms for sorting and searching operations. Game developers use STL containers to manage game objects, and financial applications use them for data analysis.

Exception Handling in C++

Exception handling in C++ is a process to handle runtime errors. It allows us to maintain the normal flow of the application even after runtime errors.

Exception Handling Keywords:

  • try - represents a block of code that can throw an exception
  • catch - represents a block of code that handles the exception
  • throw - used to throw an exception
// Exception handling example
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

// Custom exception class
class MyException : public exception {
private:
    string message;
    
public:
    MyException(const string& msg) : message(msg) {}
    
    const char* what() const noexcept override {
        return message.c_str();
    }
};

// Function that might throw an exception
double divide(double numerator, double denominator) {
    if (denominator == 0) {
        throw runtime_error("Division by zero!");
    }
    return numerator / denominator;
}

// Function that throws a custom exception
void processValue(int value) {
    if (value < 0) {
        throw MyException("Negative value not allowed!");
    }
    if (value > 100) {
        throw out_of_range("Value is out of range!");
    }
    cout << "Processing value: " << value << endl;
}

int main() {
    // Basic exception handling
    cout << "=== Basic Exception Handling ===" << endl;
    
    try {
        double result = divide(10, 2);
        cout << "10 / 2 = " << result << endl;
        
        result = divide(5, 0); // This will throw an exception
        cout << "5 / 0 = " << result << endl;
    }
    catch (const exception& e) {
        cerr << "Exception caught: " << e.what() << endl;
    }
    
    // Multiple catch blocks
    cout << "\n=== Multiple Catch Blocks ===" << endl;
    
    try {
        processValue(50);
        processValue(-5); // This will throw a custom exception
        processValue(150); // This will throw an out_of_range exception
    }
    catch (const MyException& e) {
        cerr << "Custom exception caught: " << e.what() << endl;
    }
    catch (const out_of_range& e) {
        cerr << "Out of range exception caught: " << e.what() << endl;
    }
    catch (const exception& e) {
        cerr << "Standard exception caught: " << e.what() << endl;
    }
    catch (...) {
        cerr << "Unknown exception caught" << endl;
    }
    
    // Exception safety and RAII (Resource Acquisition Is Initialization)
    cout << "\n=== RAII and Exception Safety ===" << endl;
    
    class FileHandler {
    private:
        string filename;
        
    public:
        FileHandler(const string& name) : filename(name) {
            cout << "Opening file: " << filename << endl;
            // Simulate something that might fail
            if (filename == "invalid.txt") {
                throw runtime_error("Failed to open file!");
            }
        }
        
        ~FileHandler() {
            cout << "Closing file: " << filename << endl;
        }
        
        void process() {
            cout << "Processing file: " << filename << endl;
        }
    };
    
    try {
        FileHandler file1("data.txt");
        file1.process();
        
        FileHandler file2("invalid.txt"); // This will throw an exception
        file2.process();
    }
    catch (const exception& e) {
        cerr << "File error: " << e.what() << endl;
    }
    
    // Nested try-catch blocks
    cout << "\n=== Nested Try-Catch Blocks ===" << endl;
    
    try {
        try {
            processValue(-10);
        }
        catch (const MyException& e) {
            cerr << "Inner catch: " << e.what() << endl;
            // Re-throw the exception
            throw;
        }
    }
    catch (const MyException& e) {
        cerr << "Outer catch: " << e.what() << endl;
    }
    
    cout << "\nProgram continues after exception handling." << endl;
    
    return 0;
}

Real-world Application:

Exception handling is crucial for building robust applications. It's used in database systems to handle connection failures, in file systems to handle missing files or permission issues, in network applications to handle connectivity problems, and in any application where runtime errors need to be handled gracefully without crashing the program.

File Handling in C++

C++ provides the fstream library for handling files. This library provides three classes: ofstream (output file stream), ifstream (input file stream), and fstream (file stream for both input and output).

File Operations:

  • Opening a file
  • Reading from a file
  • Writing to a file
  • Closing a file
  • File positioning
// File handling example
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

// Function to write data to a file
void writeToFile(const string& filename, const vector<string>& lines) {
    ofstream outFile;
    
    // Open file for writing (truncate if exists)
    outFile.open(filename);
    
    if (!outFile) {
        cerr << "Error opening file for writing: " << filename << endl;
        return;
    }
    
    // Write lines to file
    for (const string& line : lines) {
        outFile << line << endl;
    }
    
    outFile.close();
    cout << "Data written to file successfully." << endl;
}

// Function to read data from a file
void readFromFile(const string& filename) {
    ifstream inFile;
    
    // Open file for reading
    inFile.open(filename);
    
    if (!inFile) {
        cerr << "Error opening file for reading: " << filename << endl;
        return;
    }
    
    string line;
    cout << "File contents:" << endl;
    
    // Read file line by line
    while (getline(inFile, line)) {
        cout << line << endl;
    }
    
    inFile.close();
}

// Function to append data to a file
void appendToFile(const string& filename, const vector<string>& lines) {
    ofstream outFile;
    
    // Open file for appending
    outFile.open(filename, ios::app);
    
    if (!outFile) {
        cerr << "Error opening file for appending: " << filename << endl;
        return;
    }
    
    // Append lines to file
    for (const string& line : lines) {
        outFile << line << endl;
    }
    
    outFile.close();
    cout << "Data appended to file successfully." << endl;
}

// Function to demonstrate binary file operations
void binaryFileOperations() {
    // Write binary data
    ofstream outFile("data.bin", ios::binary);
    
    if (!outFile) {
        cerr << "Error opening binary file for writing." << endl;
        return;
    }
    
    int numbers[] = {10, 20, 30, 40, 50};
    outFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));
    outFile.close();
    
    // Read binary data
    ifstream inFile("data.bin", ios::binary);
    
    if (!inFile) {
        cerr << "Error opening binary file for reading." << endl;
        return;
    }
    
    int readNumbers[5];
    inFile.read(reinterpret_cast<char*>(readNumbers), sizeof(readNumbers));
    inFile.close();
    
    cout << "Binary data read: ";
    for (int num : readNumbers) {
        cout << num << " ";
    }
    cout << endl;
}

// Function to demonstrate file positioning
void filePositioning() {
    fstream file("position.txt", ios::in | ios::out | ios::trunc);
    
    if (!file) {
        cerr << "Error opening file for positioning." << endl;
        return;
    }
    
    // Write some data
    file << "1234567890" << endl;
    file << "ABCDEFGHIJ" << endl;
    file << "KLMNOPQRST" << endl;
    
    // Go to beginning of file
    file.seekg(0, ios::beg);
    
    // Read first line
    string line;
    getline(file, line);
    cout << "First line: " << line << endl;
    
    // Move to position 5 in the file
    file.seekg(5, ios::beg);
    char ch;
    file.get(ch);
    cout << "Character at position 5: " << ch << endl;
    
    // Move to end of file
    file.seekg(0, ios::end);
    streampos endPos = file.tellg();
    cout << "File size: " << endPos << " bytes" << endl;
    
    file.close();
}

int main() {
    string filename = "example.txt";
    
    // Write to file
    vector<string> lines = {
        "Hello, File Handling in C++!",
        "This is the second line.",
        "And this is the third line."
    };
    
    writeToFile(filename, lines);
    
    // Read from file
    readFromFile(filename);
    
    // Append to file
    vector<string> newLines = {
        "This line was appended.",
        "Another appended line."
    };
    
    appendToFile(filename, newLines);
    
    // Read again to show appended content
    readFromFile(filename);
    
    // Binary file operations
    cout << "\n=== Binary File Operations ===" << endl;
    binaryFileOperations();
    
    // File positioning
    cout << "\n=== File Positioning ===" << endl;
    filePositioning();
    
    // Error handling with files
    cout << "\n=== Error Handling ===" << endl;
    ifstream nonExistentFile("nonexistent.txt");
    
    if (!nonExistentFile) {
        cerr << "Error: Could not open nonexistent.txt" << endl;
    }
    
    // Check file state flags
    ifstream testFile("example.txt");
    
    if (testFile.good()) {
        cout << "File is in good state" << endl;
    }
    
    if (testFile.is_open()) {
        cout << "File is open" << endl;
        testFile.close();
    }
    
    return 0;
}

Real-world Application:

File handling is essential for applications that need to persist data, such as:

  1. Database systems for storing records
  2. Text editors for saving and loading documents
  3. Configuration files for storing application settings
  4. Game development for saving game progress
  5. Data logging applications for recording sensor data

Advanced C++ Topics

C++ offers several advanced features that provide more control and efficiency for complex programming tasks.

Advanced Topics:

  • Smart Pointers
  • Lambda Expressions
  • Move Semantics
  • Multithreading
  • Type Inference (auto and decltype)
  • Range-based for loops
// Advanced topics example
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

// Smart pointers example
void smartPointers() {
    cout << "=== Smart Pointers ===" << endl;
    
    // Unique pointer (unique ownership)
    unique_ptr<int> uniquePtr = make_unique<int>(42);
    cout << "Unique pointer value: " << *uniquePtr << endl;
    
    // Shared pointer (shared ownership)
    shared_ptr<int> sharedPtr1 = make_shared<int>(100);
    shared_ptr<int> sharedPtr2 = sharedPtr1;
    
    cout << "Shared pointer 1 value: " << *sharedPtr1 << endl;
    cout << "Shared pointer 2 value: " << *sharedPtr2 << endl;
    cout << "Use count: " << sharedPtr1.use_count() << endl;
    
    // Weak pointer (no ownership, avoids circular references)
    weak_ptr<int> weakPtr = sharedPtr1;
    cout << "Weak pointer use count: " << weakPtr.use_count() << endl;
    
    if (!weakPtr.expired()) {
        shared_ptr<int> sharedPtr3 = weakPtr.lock();
        cout << "Value from weak pointer: " << *sharedPtr3 << endl;
    }
}

// Lambda expressions example
void lambdaExpressions() {
    cout << "\n=== Lambda Expressions ===" << endl;
    
    // Simple lambda
    auto greet = []() {
        cout << "Hello, Lambda!" << endl;
    };
    
    greet();
    
    // Lambda with parameters
    auto add = [](int a, int b) {
        return a + b;
    };
    
    cout << "5 + 3 = " << add(5, 3) << endl;
    
    // Lambda with capture clause
    int multiplier = 3;
    auto times = [multiplier](int x) {
        return x * multiplier;
    };
    
    cout << "5 * 3 = " << times(5) << endl;
    
    // Using lambda with STL algorithms
    vector<int> numbers = {5, 2, 8, 1, 9, 3, 6, 4, 7};
    
    cout << "Numbers greater than 5: ";
    for_each(numbers.begin(), numbers.end(), [](int n) {
        if (n > 5) {
            cout << n << " ";
        }
    });
    cout << endl;
    
    // Count even numbers
    int evenCount = count_if(numbers.begin(), numbers.end(), [](int n) {
        return n % 2 == 0;
    });
    
    cout << "Count of even numbers: " << evenCount << endl;
}

// Move semantics example
void moveSemantics() {
    cout << "\n=== Move Semantics ===" << endl;
    
    // Create a vector with some data
    vector<string> source = {"Hello", "World", "Move", "Semantics"};
    cout << "Source size: " << source.size() << endl;
    
    // Move the data to a new vector
    vector<string> destination = move(source);
    
    cout << "After move:" << endl;
    cout << "Source size: " << source.size() << endl;
    cout << "Destination size: " << destination.size() << endl;
    cout << "Destination contents: ";
    
    for (const string& s : destination) {
        cout << s << " ";
    }
    cout << endl;
}

// Multithreading example
void multithreading() {
    cout << "\n=== Multithreading ===" << endl;
    
    // Function to be executed in a thread
    auto threadFunction = [](int id, int count) {
        for (int i = 0; i < count; i++) {
            cout << "Thread " << id << ": " << i << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    };
    
    // Create threads
    thread t1(threadFunction, 1, 5);
    thread t2(threadFunction, 2, 5);
    
    // Wait for threads to finish
    t1.join();
    t2.join();
    
    cout << "Threads finished execution." << endl;
}

// Type inference and range-based for loops
void modernCppFeatures() {
    cout << "\n=== Modern C++ Features ===" << endl;
    
    // Type inference with auto
    auto number = 42; // int
    auto name = "John"; // const char*
    auto price = 19.99; // double
    
    cout << "Number: " << number << endl;
    cout << "Name: " << name << endl;
    cout << "Price: " << price << endl;
    
    // Range-based for loop
    vector<int> numbers = {1, 2, 3, 4, 5};
    
    cout << "Numbers: ";
    for (auto n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    
    // With reference to modify values
    cout << "Squared numbers: ";
    for (auto& n : numbers) {
        n = n * n;
        cout << n << " ";
    }
    cout << endl;
    
    // decltype for type deduction
    decltype(numbers) anotherVector = {10, 20, 30};
    cout << "Another vector: ";
    
    for (auto n : anotherVector) {
        cout << n << " ";
    }
    cout << endl;
}

int main() {
    smartPointers();
    lambdaExpressions();
    moveSemantics();
    multithreading();
    modernCppFeatures();
    
    return 0;
}

Real-world Application:

Advanced C++ features are used in:

  1. High-performance applications (move semantics, smart pointers)
  2. Concurrent programming (multithreading)
  3. Functional programming patterns (lambda expressions)
  4. Generic programming (type inference)
  5. Modern codebases that require memory safety and performance