Core C++ Knowledge Summary: Syntax, Memory, and Modern Features

Introduction

This document summarizes essential C++ concepts including syntax, memory management, and object-oriented programming.

1. C++ Fundamentals

1.1 Pointers and References

Differences Between Pointers and References

A pointer stores the address of an object. Its itself a variable (a named object) and has its own address, allowing pointers to pointers to exist. Pointers are mutable, meaning both the address they hold and the data at that address can change. A reference is an alias for a variable; it must be initialized and cannot be reassigned to refer to a different object.

  1. Definition and Declaration A pointer is a variable whose value is the address of another variable. Use the * symbol when declaring a pointer. A reference is an alias created for an existing variable. Use the & symbol when declaring a reference.

  2. Usage and Operations Pointers use the dereference operator * to access the value of the variable they point to, and the address-of operator & to get a variable's address. References are initialized upon declaration and refer to the same variable throughout their lifetime. No dereference operator is needed because a reference is the variable's alias. Pointers can have multiple levels (e.g., pointer to pointer), while references have only one level.

  3. Null Values A pointer can be null (nullptr), indicating it points to no valid address. A reference must be initialized upon declaration and cannot be rebound. There is no concept of a null reference.

  4. Mutability A pointer's target can be changed to point to a different memory address. Once a reference is initialized, it always refers to the same object and cannot be rebound.

  5. Use Cases Use pointers when you need to return the memory of a local variable inside a function. Dynamic memory allocated with new must be explicitly freed to avoid memory leaks. Returning a reference to a local variable is meaningless. Use references when stack space sensitivity is important (e.g., recursion). Passing by reference avoids creating temporary objects, incurring less overhead. Use references when passing class objects as parameters; this is the standard idiom in C++.

Function Pointers

Definition and Usage A function pointer is a variable that stores the address of a function. It enables dynamic selection of which function to call at runtime. The syntax is: return_type (*pointer_name)(parameter_list) Assignment methods: pointer_name = function_name; or pointer_name = &function_name;

int sum(int a, int b) {
    return a + b;
}
int diff(int a, int b) {
    return a - b;
}
int main() {
    int (*func_ptr)(int, int);
    func_ptr = ∑
    int res = func_ptr(12, 5);
    std::cout << "Result: " << res << std::endl;
    
    func_ptr = &diff;
    res = func_ptr(12, 5);
    std::cout << "Result: " << res << std::endl;
    return 0;
}

Use Cases:

  1. Callbacks: passing function addresses to other functions for later invocation.
  2. Function pointer arrays: implementing state machines that call different functions based on input.
  3. Dynamic library loading: calling functions from dynamically loaded libraries.
  4. Polymorphism: combining virtual functions with function pointers for polymorphic behavior.
  5. Function pointers as parameters: enabling pluggable function behavior.
  6. Function mapping tables: mapping conditions to specific function calls.

Function Pointer vs. Pointer Function

A function pointer is a pointer variable that points to a function.

int sum(int a, int b) { return a + b; }
int (*fptr)(int, int) = &sum;
int val = (*fptr)(3, 4);

A pointer function is a function that returns a pointer.

int* fetchPointer() {
    int x = 10;
    return &x; // returning address of local variable is not recommended
}

1.2 Data Types

Integer Types: short, int, long, long long

C++ integer type length standards:

  • short: at least 16 bits
  • int: at least as long as short
  • long: at least 32 bits, and at least as long as int
  • long long: at least 64 bits, and at least as long as long

On systems with 8-bit bytes, 1 byte = 8 bits. Many systems use minimum lengths: short is 16 bits (2 bytes), long is 32 bits (4 bytes), long long is 64 bits (8 bytes). int is often 4 bytes (same as long). Use the sizeof operator to check data type sizes, e.g., sizeof(int). The <climits> header defines symbolic constants like INT_MAX and INT_MIN.

Unsigned Types

Unsigned types store non-negative integers, which increases the maximum value without changing the data size. int is typically chosen as the natural type for efficiency.

Explicit Type Conversions

Keywords: static_cast, dynamic_cast, reinterpret_cast, const_cast

static_cast

  • No runtime type checking for safety.
  • Safe for upcasting (derived to base).
  • Unsafe for downcasting (base to derived) due to lack of dynamic type checking.
  • Used for basic type conversions (e.g., int to char) and converting any type to void.

dynamic_cast

  • Provides type checking for downcasting (relies on virtual functions), safer than static_cast.
  • The target must be a pointer, reference, or void*; the base class must have virtual functions.
  • For pointers, failure returns nullptr; for references, failure throws an exception.

reinterpret_cast

  • Converts between integer types and pointers, or between different pointer types. Platform-dependent and risky.

const_cast

  • Removes const or volatile qualifiers. Converts constant pointers/references to non-constant ones, still pointing to the original object.

Comparing Floating-Point Numbers

Direct equality (==) is unreliable due to representation imprecision. Compare the absolute difference against a predefined epsilon threshold. Comparing with zero requires the same caution.

1.3 Keywords

const

Purpose of const The const keyword specifies immutability for variables, pointers, references, and member functions.

  1. Constant Variables: Declares constants that must be initialized and cannot be modified later.
  2. Member Variables: Must be initialized via constructor initializer list; cannot be initialized inside the class definition.
  3. Member Functions: Declares that the function does not modify object state (for non-mutable members). const objects cannot call non-const member functions.
  4. Constant Objects: The object's members cannot be modified.
  5. const Parameters: Can accept both const and non-const arguments, indicating the function won't modify the passed argument.

Pointer to Constant (Low-level const) A pointer to a read-only object. The value of the object cannot be modified through this pointer. Syntax: const type *ptr = &var; or type const *ptr = &var;

int val = 10;
const int* ptr_a = &val;
// *ptr_a = 9;  // Error: read-only location
val = 9;        // OK

Constant Pointer (Top-level const) A pointer whose address cannot be changed after initialization. Syntax: type * const ptr = &var;

int val1 = 10;
int val2 = 12;
int* const ptr_b = &val1;
// ptr_b = &val2; // Error: cannot change pointer target
*ptr_b = 9;       // OK

Rule of Thumb: const left of * means pointer to constant; const right of * means constant pointer. Use volatile to modify a const variable's value. Use mutable on data members that need modification inside const member functions.

static

The static keyword controls variable/function lifetime, scope, and access.

  1. Static Local Variables: Defined inside a function with static. Lifetime is the entire program; initialized only once.
void counterFunc() {
    static int cnt = 0;
    cnt++;
    std::cout << "Count: " << cnt << std::endl;
}
  1. Static Member Variables: Shared across all objects of the class. Must be defined outside the class.
class Example {
public:
    static int sharedVar;
};
int Example::sharedVar = 0;
  1. Static Member Functions: Belong to the class, not an object. Cannot access non-static members directly.
class Example {
public:
    static void helper() {
        std::cout << "Static method" << std::endl;
    }
};

const vs. static

const ensures immutability, while static affects lifetime and sharing.

#define vs. typedef

#define is simple text substitution with no type checking, processed during preprocessing. typedef creates an alias with type checking, processed during compilation.

#define vs. inline

#define performs text substitution without type safety. inline functions are compiled, type-checked, and may reduce function call overhead.

#define vs. const

const defines a typed constant at compile time, stored in memory. #define is an untyped macro, substituted at preprocessing.

constexpr

constexpr indicates a compile-time constant. It implies const, but not all const variables are constexpr. constexpr functions can be evaluated at compile time.

constexpr int maxItems = 100;
constexpr int total = maxItems + 50;

volatile

Informs the compiler that a variable may change unexpectedly (e.g., by hardware or another thread). Every access reads from memory directly, disabling certain optimizations.

mutable

Allows a data member to be modified inside a const member function.

class Entity {
    int id_;
    mutable int accessCount_;
public:
    void display() const {
        // id_ = 10; // Error
        accessCount_ = 20; // OK
    }
};

explicit

Prevents implicit conversions for constructors (usually single-argument constructors).

extern

Declares a global variable defined in another translation unit. extern "C" enables C linkage for interoperability.

Prefix vs. Postfix Increment/Decrement

struct Iterator {
    Iterator& operator++() { // Prefix
        ++ptr_;
        return *this;
    }
    const Iterator operator++(int) { // Postfix
        Iterator tmp = *this;
        ++*this;
        return tmp;
    }
private:
    int* ptr_;
};

Postfix returns by value (old state), prefix returns by reference (new state). Prefix is often more efficient.

std::atomic

Operations like counter++ or x = y are not inherently thread-safe. std::atomic provides atomic operations on integral types, avoiding data races.

std::atomic<int> safeCounter;
safeCounter = 99;

1.4 struct vs. class

  • struct members default to public; class members default to private.
  • struct inheritance defaults to public; class inheritance defaults to private.
  • Use struct for simple data aggregates and class for encapsulated objects.

1.5 Storage Class: Static Local, Global, and Local Variables

  • Static Local: Scope limited to function, lifetime equals program duration, value persists between calls.
  • Global: Scope is the entire program, lifetime equals program duration.
  • Local: Scope limited to enclosing block, lifetime ends when the block exits.

1.6 Code Before and After main()

Before main: Stack pointer setup, initialization of static and global variables (.data, .bss), global object constructors, passing argc/argv. After main: Global object destructors, functions registered with atexit().

1.7 Exception Handling

Use try, throw, and catch to manage runtime errors like division by zero or out-of-bounds access.

try {
    if (denominator == 0)
        throw std::runtime_error("Division by zero");
    result = numerator / denominator;
} catch (const std::exception& e) {
    std::cerr << "Caught: " << e.what() << std::endl;
}

C++ standard exception hierarchy includes bad_alloc, out_of_range, bad_cast, etc.

1.8 Function Parameters

Formal parameters are placeholders in the function definition. Actual parameters are the values passed during the call.

Passing Mechanisms:

  1. By Value: Copies data; safe but potentially expensive.
  2. By Pointer: Copies address (4/8 bytes); allows modifying original data.
  3. By Reference: Passes alias; efficient and allows modification. Preferred for large objects.

1.9 Compilation Process

  1. Preprocessing: Handles #include, #define, conditionals, generates .i/.ii files.
  2. Compilation: Translates to assembly (.s files).
  3. Assembly: Converts to machine code object files (.o/.obj).
  4. Linking: Combines object files and libraries into an executable.

1.10 Static vs. Dynamic Linking

Static Linking: Library code is copied into the executable at compile time. Dynamic Linking: Shared libraries (.so/.dll) are loaded at runtime, saving disk space and memory through sharing.

1.11 How "hello world" Reaches the Screen

The operating system loads the executable, creates a process, allocates memory, handles page faults, executes the code, makes system calls to display drivers, and finally the monitor displays the output.

1.12 Core Dumps

A core dump is a file recording a process's memory state when it crashes. It can be analyzed with a debugger like GDB to diagnose the failure.

2. C++ Memory Management

2.1 Memory Layout

  1. Stack: Local variables, function call management. Automatic allocation.
  2. Heap: Dynamically allocated memory (new/malloc). Manually managed.
  3. Global/Static Area: Global and static variables.
  4. Constant Area: Read-only constants like string literals.
  5. Code Area: Executable code.

Heap vs. Stack

Feature Heap Stack
Management Manual by programmer Automatic by compiler
Allocation Uses free list, potential fragmentation Continuous space, simple pointer update
Size Limit Limited by virtual memory Predefined size (e.g., 2MB)
Growth Direction Grows towards higher addresses Grows towards lower addresses
Efficiency Slower due to complex management Faster due to hardware support

2.2 Memory Leaks

A memory leak occurs when dynamically allocated memory is no longer reachable and not freed. Smart pointers (std::unique_ptr, std::shared_ptr) help prevent leaks. Destructors in base classes should be virtual when deleting derived objects through base pointers.

2.3 Smart Pointers

  • std::unique_ptr: Exclusive ownership. Movable, not copyable.
  • std::shared_ptr: Shared ownership with reference counting.
  • std::weak_ptr: Non-owning observer to break shared_ptr cycles.
#include <memory>
auto uniquePtr = std::make_unique<Widget>(42);
auto sharedPtr1 = std::make_shared<Widget>(42);
std::shared_ptr<Widget> sharedPtr2 = sharedPtr1;
std::weak_ptr<Widget> weakPtr = sharedPtr1;

2.4 new/delete vs. malloc/free

  • new throws std::bad_alloc on failure; malloc returns NULL.
  • new automatically calculates size; malloc requires explicit size.
  • new calls constructors; malloc does not.
  • new/delete are operators; malloc/free are functions.

2.5 Dangling Pointers

Pointers that retain the address of freed memory. Best mitigated by setting pointers to nullptr after deletion and using smart pointers.

2.6 Memory Alignment

Data alignment ensures variables start at addresses that are multiples of their size, improving CPU memory access efficiency. Structures may include padding to satisfy alignment requirements for all members.

alignas can specify alignment; alignof queries it.

2.7 Out-of-Order Execution

Modern CPUs may reorder instructions for performance. C++ memory model and std::atomic provide barriers to enforce ordering in multi-threaded code.

2.8 Common String Operations

Standard functions like strcpy, strlen, strcat, strcmp can be manually implemented for educational purposes and understanding memory overlap concerns.

char* customCopy(char* destination, const char* source) {
    assert(destination && source);
    char* start = destination;
    while ((*destination++ = *source++) != '\0');
    return start;
}

2.9 Declaration vs. Definition

A declaration introduces a name and type, while a definition allocates storage. Multiple declarations are allowed, but only one definition exists for each entity.

2.10 Zero-Copy

Zero-copy techniques reduce CPU involvement in data transfers. std::vector::emplace_back constructs elements in-place, avoiding unnecessary copies or moves.

2.11 Function Call Stack Process

When a function is called, parameters are pushed onto the stack right-to-left, followed by the return address. Local variables are then allocated on the stack.

2.12 Using memset(this, 0, sizeof(*this)) in Constructors

This approach is dangerous if the class contains virtual functions (destroys the vtable pointer) or C++ objects with dynamic resources (corrupts their state).

3. Object-Oriented Programming in C++

3.1 Three Pillars of OOP

  1. Inheritance: Deriving new classes from existing ones, reusing and extending functionality.
  2. Encapsulation: Bundling data and methods, restricting access via public, protected, private.
  3. Polymorphism: Calling derived class methods through base class interfaces. Achieved via function overloading (compile-time) and virtual functions (runtime).

3.2 Access Specifiers and Inheritance

  • public: Accessible anywhere.
  • protected: Accessible within the class and its derived classes.
  • private: Accessible only within the class itself.

Inheritance mode (public, protected, private) can further restrict base member access in the derived class.

3.3 Multiple Inheritance and Virtual Inheritance

Multiple inheritance allows a class to derive from more than one base class. The Diamond Problem occurs when a class indirectly inherits the same base twice; virtual inheritance solves this by ensuring only one shared base sub-object.

class Base { public: int data; };
class DerivedA : virtual public Base {};
class DerivedB : virtual public Base {};
class Final : public DerivedA, public DerivedB {};

3.4 Overloading vs. Overriding

  • Overloading: Same function name, different parameter lists, resolved at compile time.
  • Overriding: Redefining a virtual function in a derived class, resolved at runtime. Function signature must match.

3.5 How Polymorphism Works

Polymorphism relies on virtual function tables (vtables). Each polymorphic class has a vtable containing pointers to its virtual functions; each object has a vptr pointing to the correct vtable.

3.6 Static vs. Non-static Members

Non-static members belong to objects; static members belong to the class. Static member functions cannot access non-static members directly as they lack a this pointer.

3.7 Constructors and Destructors

  • Constructors: Initialize objects. Types include default, parameterized, copy, move, and delegating.
  • Destructors: Clean up resources. Base class destructors should be virtual when inheritance and polymorphism are used.

3.8 Virtual Functions

  • Virtual Functions: Allow derived classes to provide specific implementations. Use the virtual keyword.
  • Pure Virtual Functions: virtual void func() = 0; Makes the class abstract and non-instantiable.
  • Virtual Destructors: Ensure derived destructors are called when deleting objects through base pointers.

Constructors cannot be virtual; virtual functions should generally not be called within constructors or destructors as the dynamic binding may not behave as expected.

3.9 Deep Copy vs. Shallow Copy

  • Deep Copy: Duplicates not only the object but also dynamically allocated resources.
  • Shallow Copy: Copies member values only, potentially leading to two objects sharing the same resource.

3.10 Operator Overloading

Operators can be overloaded as member or non-member functions. Follow established conventions for symmetry and clarity. Lambdas are essentially objects with an overloaded operator().

3.11 Initialization Lists

Using the constructor initializer list is more efficient than assignment within the constructor body, and it is required for initializing references, const members, and base classes that lack default constructors.

3.12 Static vs. Dynamic Types and Binding

  • Static Type: The type known at compile time.
  • Dynamic Type: The actual derived type at runtime.
  • Static Binding: Resolved at compile time (non-virtual functions).
  • Dynamic Binding: Resolved at runtime via virtual functions.

3.13 Inheritance vs. Composition

  • Inheritance (is-a): Causes tight coupling; changes to base can affect derived classes.
  • Composition (has-a): Promotes loose coupling; objects can be replaced dynamically.

3.14 Default Functions in an Empty Class

An empty class in C++ will have a compiler-generated default constructor, copy constructor, destructor, and copy assignment operator.

3.15 The this Pointer

this is an implicit pointer to the current object inside non-static member functions. It points to the object's starting address, enabling access to member variables.

3.16 Calling delete this

Dangerous. It destroys the object while it might still be in use. If called directly in a destructor, it causes infinite recursion and a stack overflow.

3.17 Object Size Calculation

  • Empty class: 1 byte (to ensure unique addresses).
  • Class with virtual functions: includes a vptr (4 or 8 bytes).
  • Static members: stored outside the object, do not affect size.

4. C++ Standard Template Library (STL)

4.1 STL Overview

STL consists of six components: Containers, Algorithms, Iterators, Functors, Adapters, and Allocators.

4.2 Core Containers

  • Sequence: vector, list, deque, array, forward_list.
  • Associative: set, multiset, map, multimap, unordered variants.
  • Adapters: stack, queue, priority_queue.

std::vector Internals

A dynamic array that grows by reallocating a larger block when capacity is exceeded. Common strategies include doubling the capacity. Operations like resize change the number of elements, while reserve changes the capacity.

std::list

A doubly-linked list. Insertions and deletions are O(1) anywhere, but random access is not supported.

Feature std::vector std::list
Access Fast random access O(1) No random access (sequential)
Insert/Delete O(1) at end, O(n) elsewhere O(1) everywhere
Memory Contiguous block, may overallocate Non-contiguous nodes

std::deque

Double-ended queue composed of multiple memory chunks, supporting fast insertions at both ends and random access.

std::map vs. std::unordered_map

  • map: Implemented as a red-black tree, ordered keys, O(log n) operations.
  • unordered_map: Hash table, average O(1) operations, unordered keys.

push_back vs. emplace_back

emplace_back constructs an element in-place using forwarded arguments, avoiding a temporary object creation.

4.3 Iterators

Iterators abstract the traversal of container elements. Erasing elements can invalidate iterators depending on the container type (e.g., vector iterators are invalidated).

4.4 std::string and const char* Conversions

std::string s = "abc";
const char* c_str = s.c_str(); // string to const char*

const char* literal = "abc";
std::string s2(literal); // literal to string

4.5 Traits Technique

Traits provide compile-time type information. iterator_traits deduces associated types like value_type; type_traits inspects class properties like trivial constructors/destructors to optimize algorithms.

4.6 Trivial Destructors

A destructor automatically generated by the compiler. STL allocators can optimize by skipping destructor calls for trivially destructible types.

4.7 RAII (Resource Acquisition Is Initialization)

Resources are acquired in a constructor and released in the destructor, tying resource lifecycle to object scope. Smart pointers are a prime example.

4.8 Two-Level Allocator

STL uses a two-tier allocator. For blocks larger than 128 bytes, malloc/free are used directly. For smaller blocks, a memory pool of fixed-size free lists (multiples of 8 bytes, up to 128) manages allocations to reduce fragmentation and overhead.

4.9 Hash Table Collision Resolution

Common strategies include chaining (separate lists) and open addressing (linear, quadratic, double hashing).

5. C++ Templates and Generic Programming

5.1 Full and Partial Specialization

Templates can be specialized for specific types to provide more efficient implementations. Function templates support only full specialization; class templates support both full and partial specialization.

5.2 Template Implementation

The compiler generates a concrete function or class from a template when instantiated. The template definition must be visible in the translation unit where it is used.

6. Modern C++ Features

6.1 C++11 Highlights

  • Type Deduction: auto and decltype.
  • Smart Pointers: unique_ptr, shared_ptr, weak_ptr.
  • Move Semantics: Rvalue references Type&& and std::move for efficient resource transfer.
  • Perfect Forwarding: std::forward.
  • Lambda Expressions: Inline anonymous functions.
  • Uniform Initialization: Braced initializer lists.
  • Null Pointer: nullptr to replace NULL.
  • Range-Based for Loops.

6.2 Smart Pointers in Detail

  • shared_ptr: maintains a reference count for shared ownership.
  • unique_ptr: exclusive ownership.
  • weak_ptr: observers a shared_ptr without affecting its reference count, breaking cycles.

6.3 Type Deduction: auto and decltype

auto deduces types like template argument deduction, stripping references and cv-qualifiers. decltype yields the exact type of an expression, preserving references and cv-qualifiers.

6.4 Rvalue References and Move Semantics

An rvalue reference (T&&) binds to temporary objects. std::move casts an lvalue to an rvalue, enabling the transfer of resources via move constructors.

std::string source = "temporary";
std::string dest = std::move(source); // source is now in a valid but unspecified state

6.5 nullptr

nullptr is a distinct type (std::nullptr_t) that avoids ambiguity with integers, solving a long-standing overloading problem.

6.6 Range-based for Loop

Simplifies container iteration:

std::vector<int> values = {1, 2, 3};
for (auto& elem : values) {
    std::cout << elem << std::endl;
}

6.7 Uniform Initialization

Braced initialization {} prevents narrowing conversions and provides a consistent syntax.

6.8 Lambda Expressions

[captures](parameters) -> return_type { body }. Captures allow the lambda to access surrounding variables by value or reference.

6.9 Concurrency

  • std::thread: creates a thread. Ensure thread object outlives the execution or detach() it.
  • std::mutex with std::lock_guard (simple, scoped locking) and std::unique_lock (deferred locking, movable, used with condition variables).
  • Condition variables synchronize threads based on logical conditions.
  • Read-Write locks allow concurrent reads but exclusive writes.

Tags: C++ Memory Management STL OOP C++11

Posted on Tue, 26 May 2026 18:46:23 +0000 by melittle