Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Core C++ Programming Fundamentals and Practical Usage Guide

Tech 1

Memory Leaks

Memory leaks occur when a program fails to deallocate dynamically allocated heap memory that is no longer needed, resulting in permanent memory wastage during runtime. This does not mean physical memory is lost, but that the application loses all references to an allocated memory block, making it impossible to return the memory to the system for reuse without native garbage collection, which is not available in standard C/C++.

Variable memory in C/C++ is allocated via three primary methods: stack allocation (automatic management for local variables), global/static memory allocation, and heap allocation. Memory leaks exclusively affect heap-allocated memory, as stack and static memory are automatically managed by the compiler and runtime.

To manually release heap memory when it is no longer needed, use the delete operator for single valuees, or delete[] for arrays:

int* dynamic_int = new int;
*dynamic_int = 42;
std::cout << *dynamic_int << std::endl;
delete dynamic_int;

Pseudo-Random Number Generation

The C++ standard library provides the rand() function in the <cstdlib> header to generate pseudo-random integers:

#include <iostream>
#include <cstdlib>

int main() {
    std::cout << rand() << std::endl;
    return 0;
}

To generate range-bound random integers, use the modulo (%) operator:

  • rand() % 10 returns a value between 0 and 9 inclusive
  • 13 + rand() % 51 returns a value between 13 and 63 inclusive
int main() {
    for (int iter = 0; iter < 10; iter++) {
        std::cout << 1 + (rand() % 5) << std::endl;
    }
    return 0;
}

By default, rand() uses a fixed seed value, so it produces identical sequences of values on every program run. To generate unique sequences, use the srand() function to seed the random number generator, typically with the current system time from the <ctime> header:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand((unsigned int)time(NULL));
    int random_val = rand();
    std::cout << random_val << std::endl;
    return 0;
}

Functon Overloading

C++ allows multiple functions with the same identifier in the same scope, as long as thier parameter lists differ in count, type, or order of types. Return type alone is not sufficient to overload functions:

#include <iostream>

void printValue(int val) {
    std::cout << "Integer value: " << val << std::endl;
}

void printValue(float val) {
    std::cout << "Floating-point value: " << val << std::endl;
}

int main() {
    int int_input = 45;
    float float_input = 72.341f;
    printValue(int_input);
    printValue(float_input);
    return 0;
}

Invalid overload example (only return type differs):

// Compilation error: ambiguous function signature
int getValue(int x) { return x; }
float getValue(int x) { return (float)x; }

Recursion

Recursive functions call themselves during execution, and require a base termination case to prevent infinite recursion:

int calculateFactorial(int n) {
    if (n <= 1) {
        return 1;
    }
    return n * calculateFactorial(n - 1);
}

int main() {
    std::cout << calculateFactorial(4) << std::endl; // Outputs 24
    return 0;
}

Passing Arrays to Functions

Arrays passed to functions decay to pointers to their first element, so you must also pass the array size as a separate parameter:

void printArrayElements(int arr[], int length) {
    for (int i = 0; i < length; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int calculateArraySum(int arr[], int length) {
    int total = 0;
    for (int i = 0; i < length; i++) {
        total += arr[i];
    }
    return total;
}

int main() {
    int test_arr[5] = {12, 8, 19, 3, 27};
    printArrayElements(test_arr, 5);
    std::cout << "Sum: " << calculateArraySum(test_arr, 5) << std::endl;
    return 0;
}

Function Parameter Passing Modes

Pass-by-value

Copies the argument value to the function parameter, so modifications inside the function do not affect the original variable:

void modifyValue(int val) {
    val = 100;
}

int main() {
    int original = 20;
    modifyValue(original);
    std::cout << original << std::endl; // Outputs 20, unchanged
    return 0;
}

Pass-by-reference

Uses pointers or C++ references to allow the function to modify the original argument:

void modifyValue(int* val_ptr) {
    *val_ptr = 100;
}

int main() {
    int original = 20;
    modifyValue(&original);
    std::cout << original << std::endl; // Outputs 100, modified
    return 0;
}

Object-Oriented Programming Basics

Object-oriented programming models software as interacting objects that mirror real-world entities, each with unique identity, state (attributes), and behavior (methods). Classes are blueprints for creating objects, defining the shared attributes and methods all instances of the class will have:

#include <iostream>
#include <string>

class BankAccount {
public:
    void greetUser() {
        std::cout << "Welcome to your bank account!" << std::endl;
    }
};

int main() {
    BankAccount user_account;
    user_account.greetUser();
    return 0;
}

Abstraction and Encapsulation

Abstraction exposes only essential public interfaces to external code, hiding implementation details. Encapsulation bundles data and methods into a class, restricting direct access to internal data via access specifiers: public, protected, private:

class Student {
private:
    std::string student_name;
public:
    void setName(std::string name) {
        student_name = name;
    }
    std::string getName() {
        return student_name;
    }
};

int main() {
    Student s1;
    s1.setName("Alice Johnson");
    std::cout << s1.getName() << std::endl;
    return 0;
}

Constructors

Special member functions executed automatically when a class instance is created, with no return type and the same name as the class. Can be overloaded to accept parameters for initializing attributes:

class Student {
private:
    std::string student_name;
public:
    Student(std::string name) {
        student_name = name;
    }
    std::string getName() {
        return student_name;
    }
};

int main() {
    Student s1("Bob Smith");
    std::cout << s1.getName() << std::endl;
    return 0;
}

Splitting Classes Across Files

Header files (.h/.hpp) contain class declarations and method prototypes, while source files (.cpp) contain method implementations. Include guards prevent duplicate header inclusion: MyClass.h

#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass {
public:
    MyClass();
    void printMessage();
    ~MyClass();
};

#endif

MyClass.cpp

#include "MyClass.h"
#include <iostream>

MyClass::MyClass() {
    std::cout << "Constructor called" << std::endl;
}

void MyClass::printMessage() {
    std::cout << "Hello from MyClass" << std::endl;
}

MyClass::~MyClass() {
    std::cout << "Destructor called" << std::endl;
}

Destructors

Special member functions called automatically when an object is destroyed, prefixed with ~, no parameters or return type, used to release allocated resources.

Member Access

Use the . operator for object instances, and the -> operator for pointers to objects.

Const Objects and Methods

Const-qualified objects cannot be modified after initialization. Only const-qualified methods can be called on const objects, as they guarantee not to modify object state:

class MyClass {
public:
    void print() const {
        std::cout << "Const method called" << std::endl;
    }
};

int main() {
    const MyClass obj;
    obj.print();
    return 0;
}

Member Initializer Lists

Used to initialize class members (especially const members and reference types) before the constructor body executes:

class MyClass {
private:
    int regular_var;
    const int const_var;
public:
    MyClass(int a, int b) : regular_var(a), const_var(b) {}
};

Composition

Classes can contain instances of other classes as members, modeling "has-a" relationships between objects:

class Birthday {
private:
    int month, day, year;
public:
    Birthday(int m, int d, int y) : month(m), day(d), year(y) {}
    void printDate() const {
        std::cout << month << "/" << day << "/" << year << std::endl;
    }
};

class Person {
private:
    std::string full_name;
    Birthday birth_date;
public:
    Person(std::string name, Birthday bd) : full_name(name), birth_date(bd) {}
    void printInfo() const {
        std::cout << "Name: " << full_name << std::endl;
        std::cout << "Birthdate: ";
        birth_date.printDate();
    }
};

int main() {
    Birthday bd(12, 15, 1998);
    Person p("Charlie Davis", bd);
    p.printInfo();
    return 0;
}

Friend Functions and Classes

Declared with the friend keyword inside a class, allowing external functions or other classes to access its private and protected members:

class MyClass {
private:
    int secret_val = 0;
    friend void accessSecret(MyClass& obj);
};

void accessSecret(MyClass& obj) {
    obj.secret_val = 99;
    std::cout << obj.secret_val << std::endl;
}

this Pointer

Implicit pointer available in all non-static member functions, pointing to the current object instance. Can be used to access members, resolve name conflicts, or return the current object:

class MyClass {
private:
    int val;
public:
    MyClass(int val) : val(val) {}
    void printVal() {
        std::cout << val << std::endl;
        std::cout << this->val << std::endl;
        std::cout << (*this).val << std::endl;
    }
};

Operator Overloading

Most C++ operators can be overloaded to work with user-defined types, using the operator keyword:

class Vector2D {
public:
    int x, y;
    Vector2D(int x = 0, int y = 0) : x(x), y(y) {}
    Vector2D operator+(const Vector2D& other) {
        return Vector2D(x + other.x, y + other.y);
    }
};

int main() {
    Vector2D v1(2, 3), v2(4, 5);
    Vector2D sum = v1 + v2;
    std::cout << sum.x << ", " << sum.y << std::endl; // Outputs 6, 8
    return 0;
}

Inheritance

Allows creating derived classes that inherit attributes and methods from a base class, promoting code reuse. Access specifiers for inheritance (public, protected, private) control access to inherited members:

class Animal {
public:
    void makeSound() {
        std::cout << "Generic animal sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() {
        std::cout << "Woof!" << std::endl;
    }
};

Protected members are accessible within the class and its derived classes, but not from external code.

Derived class constructor/destructor execution order: base class constructor runs first, then derived class constructor. Destructors run in reverse order: derived class first, then base class.

Polymorphism

Allows calling the same method on different derived class objects via base class pointers/references, executing the implementation matching the actual object type. Uses virtual functions in the basee class:

class Shape {
public:
    virtual double calculateArea() = 0; // Pure virtual function, makes Shape an abstract class
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double calculateArea() override {
        return 3.14159 * radius * radius;
    }
};

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

int main() {
    Shape* s1 = new Circle(5);
    Shape* s2 = new Rectangle(4, 6);
    std::cout << s1->calculateArea() << std::endl; // ~78.54
    std::cout << s2->calculateArea() << std::endl; // 24
    delete s1;
    delete s2;
    return 0;
}

Abstract classes contain pure virtual functions and cannot be instantiated, only used as base classes for derived types.

Function Templates

Allow writing generic functions that work with multiple data types without rewriting code for each type:

template <typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << add<int>(3, 5) << std::endl; // 8
    std::cout << add<double>(2.5, 4.7) << std::endl; // 7.2
    return 0;
}

Class Templates

Generic class blueprints that work with arbitrary data types:

template <typename T>
class Box {
private:
    T content;
public:
    Box(T val) : content(val) {}
    T getContent() { return content; }
};

int main() {
    Box<int> int_box(10);
    Box<std::string> str_box("Test");
    return 0;
}

Template Specialization

Allows overriding template behavior for specific data types:

template <typename T>
class TypeChecker {
public:
    TypeChecker(T val) {
        std::cout << val << " is not a character" << std::endl;
    }
};

template <>
class TypeChecker<char> {
public:
    TypeChecker(char val) {
        std::cout << val << " is a character" << std::endl;
    }
};

Exception Handling

Uses try, throw, catch blocks to handle runtime errors gracefully without crashing the program:

int main() {
    try {
        int divisor;
        std::cout << "Enter divisor: ";
        std::cin >> divisor;
        if (divisor == 0) {
            throw std::runtime_error("Division by zero is not allowed");
        }
        std::cout << "Result: " << 100 / divisor << std::endl;
    } catch (std::exception& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
    return 0;
}

File I/O

Uses the <fstream> library to read from and write to files. Types include ofstream (write), ifstream (read), fstream (read/write):

int main() {
    // Write to file
    std::ofstream out_file("output.txt");
    if (out_file.is_open()) {
        out_file << "Hello, file!" << std::endl;
        out_file.close();
    }

    // Read from file
    std::string line;
    std::ifstream in_file("output.txt");
    if (in_file.is_open()) {
        while (std::getline(in_file, line)) {
            std::cout << line << std::endl;
        }
        in_file.close();
    }
    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.