Programming Languages
C++ Questions
A comprehensive guide to C++ interview questions covering core syntax, object-oriented programming, and memory management. Essential for developers preparing for technical rounds.
Basic C++ Concepts20
C++ is a cross-platform language that was created as an extension of C. While C is procedural, C++ is a multi-paradigm language that supports Object-Oriented Programming (OOP), allowing for features like classes, objects, inheritance, and polymorphism that aren't available in standard C.
C++ features include its mid-level nature, object-oriented principles (encapsulation, abstraction, inheritance, polymorphism), rich Standard Template Library (STL), manual memory management through pointers, and strong typing. It is highly portable and offers low-level manipulation similar to assembly while maintaining high-level abstractions.
The primary difference is the default access level. In a 'struct', members are public by default, whereas in a 'class', members are private by default. Additionally, when inheriting, a struct defaults to public inheritance, while a class defaults to private inheritance.
Access specifiers define the scope and visibility of class members. 'Public' allows access from outside the class; 'Private' restricts access to within the class only; and 'Protected' allows access within the class and its derived (child) classes, facilitating controlled data exposure.
Encapsulation is the process of bundling data and methods that operate on that data into a single unit called a class. It helps in data hiding by making member variables private and providing public getter/setter methods, ensuring that the internal state of an object is protected.
Abstraction is the concept of showing only the essential features of an object to the outside world while hiding the background implementation details. In C++, this is typically achieved using header files, abstract classes, and access specifiers to simplify complex systems for the user.
Inheritance allows a new class (derived class) to inherit the properties and behaviors of an existing class (base class). This promotes code reusability and establishes a 'is-a' relationship, enabling developers to build complex hierarchies without rewriting common logic multiple times.
Polymorphism means 'many forms' and allows a single interface to represent different underlying forms (data types or functions). In C++, it is divided into compile-time polymorphism (function/operator overloading) and runtime polymorphism (virtual functions), allowing objects of different classes to be treated through a common pointer.
C++ supports five types of inheritance: Single Inheritance (one parent), Multiple Inheritance (two or more parents), Multilevel Inheritance (derived from a derived class), Hierarchical Inheritance (one parent, multiple children), and Hybrid Inheritance (a combination of two or more types).
In public inheritance, public/protected members of the base stay public/protected in the derived class. In private inheritance, all base members become private in the derived class. In protected inheritance, both public and protected base members become protected in the derived class, restricting further external access.
A constructor is a special member function that initializes an object of its class. Types include Default Constructor (takes no arguments), Parameterized Constructor (takes arguments), and Copy Constructor (initializes an object using another object of the same class). It has no return type.
A destructor is a member function that is automatically called when an object goes out of scope or is explicitly deleted. It has the same name as the class preceded by a tilde (~), takes no parameters, and is used to release resources like memory or file handles.
A constructor is invoked during object creation to allocate and initialize resources, whereas a destructor is invoked during object destruction to deallocate and clean up resources. A class can have multiple overloaded constructors, but it can only have one destructor.
No, constructors cannot be virtual in C++. This is because virtuality relies on the existence of a virtual table (vtable) and an object instance, but the object is not fully formed until the constructor finishes its execution. However, a 'virtual clone' method can simulate this behavior.
Yes, virtual destructors are essential when dealing with inheritance. If a base class pointer points to a derived class object, declaring the base destructor as virtual ensures that the derived class destructor is called first, preventing memory leaks and ensuring proper cleanup.
A copy constructor is used to create a new object as a copy of an existing object of the same class. It usually takes a reference to a constant object as an argument (e.g., MyClass(const MyClass &other)) and is used during pass-by-value or return-by-value operations.
A default constructor is a constructor that either accepts no arguments or has default values for all its arguments. If no constructor is explicitly defined by the programmer, the C++ compiler automatically provides a simple public default constructor for the class.
Shallow copy creates a new object and copies the member values, including pointers (sharing the same memory). Deep copy allocates new memory for pointers and copies the actual values, ensuring that the two objects are independent and don't interfere with each other's data.
Operator overloading allows programmers to redefine the behavior of standard operators (like +, -, *, <<) for user-defined types (classes/structs). This makes the syntax more intuitive and allows custom objects to be used in expressions similar to built-in data types.
Function overloading allows multiple functions to have the same name within the same scope, provided they have different parameter lists (different types or number of arguments). The compiler determines which function to call based on the arguments provided during the call.
OOP Concepts20
Function overriding occurs when a derived class provides a specific implementation for a function that is already defined in its base class. To enable this, the base class function must be marked with the 'virtual' keyword, allowing for runtime polymorphism.
Overloading occurs in the same scope and is resolved at compile-time (static binding) based on signatures. Overriding occurs across inheritance boundaries and is resolved at runtime (dynamic binding) based on the actual object type, requiring the 'virtual' keyword in the base class.
A virtual function is a member function in a base class that you expect to redefine in derived classes. When you call a virtual function through a base class pointer or reference, C++ uses the vtable to ensure the derived version is executed at runtime.
A pure virtual function is a function that has no implementation in the base class and is declared by assigning it 0 (e.g., virtual void func() = 0;). It serves as a placeholder, forcing derived classes to provide their own implementation before they can be instantiated.
An abstract class is a class that contains at least one pure virtual function. You cannot create an instance (object) of an abstract class. Its primary purpose is to define an interface or a common base for other classes to inherit and implement.
C++ does not have a formal 'interface' keyword like Java or C#. Instead, an interface is simulated using an abstract class that contains only public pure virtual functions and no data members, defining a strict contract for any class that implements it.
An abstract class can have member variables and some implemented (concrete) methods, whereas a C++ interface-style class consists entirely of pure virtual functions. A class can inherit from multiple interfaces but typically only one major abstract base class for structural logic.
Early binding (static) happens at compile-time when the compiler knows exactly which function to call (e.g., overloaded functions). Late binding (dynamic) happens at runtime, where the actual function to be called is determined based on the object's type via virtual tables.
Dynamic binding is a mechanism where the address of the function to be executed is determined at runtime rather than compile-time. In C++, this is achieved using virtual functions, allowing for flexible code where base pointers can invoke derived behaviors based on the instance they point to.
Static binding refers to the resolution of function calls or variables at compile-time. Since the compiler knows all the necessary information during compilation, these calls are faster than dynamic ones. Examples include non-virtual function calls and overloaded functions.
The diamond problem occurs in multiple inheritance when a class inherits from two classes that both inherit from the same base class. This creates ambiguity because the final derived class receives two copies of the original base class's members, leading to compiler errors.
The diamond problem is resolved using 'Virtual Inheritance'. By using the 'virtual' keyword during inheritance (e.g., class B : virtual public A), C++ ensures that only one shared instance of the base class 'A' is present in the final derived class.
Virtual inheritance is a technique used in multiple inheritance to prevent multiple instances of a common base class from appearing in the inheritance hierarchy. It ensures that the base class is initialized only once by the most derived class, solving ambiguity issues.
A friend function is a non-member function that is granted access to the private and protected members of a class. It is declared inside the class using the 'friend' keyword but defined outside, which is useful for operations like operator overloading for I/O streams.
A friend class is a class that can access the private and protected members of another class. If class A is declared as a friend of class B, then all member functions of class A have full access to class B's internal data, facilitating tight coupling between related classes.
The 'this' pointer is an implicit pointer available inside non-static member functions that points to the object for which the function is being called. It is used to access member variables when they are shadowed by local parameters or to return the current object.
Yes, 'delete this' is technically legal but extremely dangerous. It should only be used if you are absolutely sure the object was allocated on the heap (using new) and no further member access or calls will happen after the deletion to avoid undefined behavior.
A static member variable is shared among all instances of a class. It is not part of the individual objects but belongs to the class itself. It must be initialized outside the class and is useful for storing data that should be common across all objects (like a counter).
A static member function is a function that belongs to the class and can be called without an instance of the class. Because it is not tied to a specific object, it does not have an implicit 'this' pointer and can only access other static members.
No, static functions cannot access non-static members directly because they do not have a 'this' pointer and are not associated with any particular instance. To access non-static members, the function would need an explicit object pointer or reference passed as a parameter.
Memory Management20
'new' is an operator that allocates memory and calls the constructor to initialize the object, whereas 'malloc' is a function that only allocates raw bytes of memory. 'new' is type-safe and returns the correct pointer type, while 'malloc' returns a void pointer.
'delete' is an operator that calls the destructor and then deallocates the memory, while 'free' is a C function that simply releases the memory without calling any destructors. 'delete' should always be used with 'new', and 'free' with 'malloc'.
A memory leak occurs when a program allocates memory on the heap but fails to deallocate it before losing the pointer to that memory. Over time, this consumes available system resources and can lead to performance degradation or application crashes.
Memory leaks can be prevented by following the RAII principle, using smart pointers (unique_ptr, shared_ptr) instead of raw pointers, and ensuring that every 'new' has a corresponding 'delete'. Tools like Valgrind can also help detect leaks during development.
Smart pointers are class templates that act like raw pointers but provide automatic memory management. They track the ownership and lifecycle of an object, ensuring that the memory is automatically released when the smart pointer goes out of scope, reducing human error.
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. It enforces single ownership, meaning it cannot be copied, only moved, ensuring no two pointers manage the same resource.
std::shared_ptr is a smart pointer that maintains a reference count of how many pointers own the same resource. The resource is only deallocated when the last shared_ptr pointing to it is destroyed or reset, allowing multiple parts of a program to share ownership safely.
std::weak_ptr is a smart pointer that holds a non-owning reference to an object managed by shared_ptr. It is used to break circular dependencies (which cause memory leaks) by allowing access to an object without increasing its reference count.
Stack memory is managed automatically by the compiler for local variables and is fast but limited in size. Heap memory is managed manually by the programmer (new/delete), is much larger, but slower to allocate and prone to fragmentation if not handled carefully.
Dynamic memory allocation is the process of allocating memory manually at runtime on the heap rather than at compile-time on the stack. This allows for flexible memory usage where the size of the data (like an array) can be determined based on user input or program state.
RAII is a core C++ programming idiom where resource management (like memory, file handles, or mutexes) is tied to object lifetime. Resources are acquired in the constructor and released in the destructor, ensuring that resources are leaked neither on normal exit nor when an exception occurs.
'delete' is used to deallocate a single object allocated with 'new', while 'delete[]' is used to deallocate an array of objects allocated with 'new[]'. Using the wrong version leads to undefined behavior because 'delete[]' specifically ensures the destructor is called for every element in the array.
Using 'delete' on an array causes undefined behavior. Typically, it only calls the destructor for the first element of the array and then attempts to free the entire block, which can lead to memory corruption, resource leaks for the remaining elements, or a program crash.
A dangling pointer is a pointer that points to a memory location that has already been deallocated. Accessing a dangling pointer causes undefined behavior because the memory may have been reassigned to a different part of the program or returned to the operating system.
A void pointer, or generic pointer, is a pointer that has no associated data type (void*). It can hold the address of any object but cannot be dereferenced directly without being cast back to a specific pointer type, as the compiler doesn't know the size of the underlying data.
A null pointer is a pointer that does not point to any valid memory location. It is typically used as a sentinel value to indicate that the pointer is not currently initialized or that a function failed to return a valid memory address.
nullptr is a type-safe keyword introduced in C++11 that represents a null pointer literal. Unlike the old NULL macro which was an integer 0, nullptr has its own type (std::nullptr_t) and cannot be accidentally confused with integer types during function overloading.
NULL is a preprocessor macro typically defined as 0 or (void*)0, which can lead to ambiguity in overloaded functions that take both integers and pointers. nullptr is a literal of type std::nullptr_t that strictly represents a pointer, providing better type safety and code clarity.
The memory of a C++ program is divided into: Text Segment (compiled code), Data Segment (initialized globals), BSS Segment (uninitialized globals), Stack (local variables/function calls), and Heap (dynamic memory). Understanding these helps in managing resource allocation and optimizing performance.
Placement new is a variation of the 'new' operator that allows you to construct an object at a specific, pre-allocated memory address. It does not allocate memory itself; instead, it simply calls the constructor to initialize an object in a buffer you provide.
Pointers and References15
A pointer is a variable that stores a memory address and can be reassigned or made null. A reference is an alias for an existing variable; it must be initialized when created, cannot be made null, and cannot be reassigned to refer to a different object.
No, C++ does not allow null references. A reference must always be bound to a valid object upon initialization. While programmers can force a null reference through pointer tricks (e.g., *ptr where ptr is null), this is considered illegal and results in undefined behavior.
No, a reference in C++ is 'immutable' in terms of what it points to. Once a reference is initialized to an object, it remains an alias for that object for its entire lifetime. Any assignment to the reference changes the value of the referred object, not the reference itself.
A pointer to a pointer is a form of multiple indirection where one pointer stores the address of another pointer. This is commonly used to modify a pointer passed to a function or to manage multi-dimensional arrays (like an array of strings).
A reference to a pointer is an alias for a pointer variable. It allows a function to modify the actual pointer passed to it (e.g., changing which address it points to) without using double pointers, resulting in cleaner and more readable syntax.
Function pointers are variables that store the address of a function. They allow functions to be passed as arguments to other functions or stored in arrays, which is essential for implementing callbacks, event handlers, and functional programming patterns in C++.
The size of a pointer depends on the system architecture. On a 32-bit system, a pointer is typically 4 bytes, while on a 64-bit system, it is 8 bytes. The size is consistent regardless of the data type the pointer points to (int, char, or a large class).
Pointer arithmetic is the ability to add or subtract integers from pointers. When an integer 'n' is added to a pointer, the pointer moves forward by n * sizeof(type) bytes. This allows for efficient traversal of arrays and contiguous memory blocks.
A const pointer (Type* const ptr) is a pointer where the address it holds cannot be changed after initialization. While you can change the value of the data being pointed to, the pointer itself is locked to that specific memory location.
A pointer to const (const Type* ptr) is a pointer that cannot be used to modify the value it points to. The pointer itself can be changed to point to a different address, but the underlying data is treated as read-only through that pointer.
A const reference (const Type& ref) is a reference that treats the object it refers to as read-only. It is widely used in function parameters to allow 'pass-by-reference' for efficiency (avoiding copies) while guaranteeing that the function will not modify the original data.
Pass by value creates a copy of the data; pass by reference creates an alias for the original data; pass by pointer passes the memory address. Reference and pointer methods are more efficient for large objects, but pointers allow for null values while references are safer.
A wild pointer is a pointer that has been declared but not yet initialized to a valid memory address or NULL. Because it contains random garbage data, dereferencing it can cause unpredictable behavior, memory corruption, or immediate program termination.
The address-of operator (&) is a unary operator that returns the memory address of its operand. It is used to initialize pointers or to pass variables to functions that expect a pointer as an argument.
The dereference operator (*) is used to access or modify the value stored at the memory address held by a pointer. It essentially tells the program to 'go to this address' and work with the data located there.
Templates and STL20
Templates are a feature that allows functions and classes to operate with generic types. This enables 'Generic Programming' where you can write a single piece of code that works for any data type (int, float, custom classes) without rewriting the logic for each one.
A function template is a generic function definition that uses placeholder types. When the function is called, the compiler generates a specific version of the function (instantiation) based on the actual data types passed as arguments.
A class template allows a class to have members of generic types. This is most commonly used in container classes (like std::vector), where the class logic for managing data is the same regardless of whether it stores integers, strings, or objects.
Template specialization allows you to define a specific implementation of a template for a particular data type. This is useful when the generic logic doesn't work or isn't efficient for a specific type (e.g., specialized behavior for 'char*' instead of generic 'T').
Templates are handled by the compiler, are type-safe, and follow scope rules. Macros are handled by the preprocessor through simple text substitution, are not type-aware, and can lead to difficult-to-debug errors due to lack of type checking.
STL (Standard Template Library) is a powerful collection of C++ template classes and functions that provide common data structures and algorithms. It is designed for high performance and efficiency, allowing developers to avoid 'reinventing the wheel' for standard tasks.
The four main components of STL are: Containers (data structures), Iterators (traversal tools), Algorithms (processing logic), and Functors (function objects). Together, they provide a cohesive framework for managing and manipulating data efficiently.
Containers are objects that store and manage collections of other objects. They are divided into sequence containers (like vector, list), associative containers (like map, set), and container adapters (like stack, queue), each optimized for different use cases.
Iterators are objects that act as a bridge between containers and algorithms. They behave similarly to pointers, allowing a program to traverse through the elements of a container in a uniform way regardless of the container's internal structure.
STL algorithms are a set of generic functions for performing tasks like searching, sorting, counting, and transforming data within containers. They work using iterators, making them highly flexible and independent of the specific container type being used.
A standard C++ array has a fixed size determined at compile-time. A std::vector is a dynamic array that can grow or shrink in size at runtime. Vectors manage their own memory automatically on the heap, while traditional arrays are often on the stack.
std::vector is a contiguous array that allows fast random access but slow insertion/deletion in the middle. std::list is a doubly-linked list that allows fast insertion/deletion at any point but slow (sequential) access to elements.
std::map is implemented as a self-balancing binary search tree, keeping elements sorted with O(log n) access time. std::unordered_map is implemented using a hash table, offering faster O(1) average access time but with no specific order of elements.
A std::set only allows unique elements and automatically sorts them. A std::multiset also keeps elements sorted but allows for duplicate values. Both are typically implemented using red-black trees, providing logarithmic time complexity for insertions and lookups.
Inserting at the end of a vector is O(1) amortized time. However, inserting at the beginning or in the middle is O(n) because all subsequent elements must be shifted in memory to accommodate the new value.
Since std::map is structured as a balanced binary search tree, the time complexity for searching, inserting, or deleting an element is O(log n), where n is the number of elements in the map.
Sequence containers implement data structures that can be accessed in a linear fashion. Common examples include std::vector, std::deque, std::list, and std::forward_list, which maintain the order in which elements are inserted.
Associative containers implement sorted data structures that can be quickly searched using keys. Examples include std::set, std::map, std::multiset, and std::multimap, which are typically based on tree structures and maintain an internal sort order.
Container adapters are not full containers themselves but provide a specific interface to an underlying sequence container. Common adapters include std::stack (LIFO), std::queue (FIFO), and std::priority_queue, which restrict access to follow specific logic.
Iterator invalidation occurs when an operation on a container (like adding or removing elements) makes an existing iterator point to an invalid memory location. For example, resizing a vector may relocate its memory, making all existing iterators pointing to it unusable.
Advanced Concepts25
Exception handling is a mechanism to handle runtime errors in a controlled way. It allows a program to 'throw' an error object when a problem occurs and 'catch' it in a different part of the code, preventing the application from crashing unexpectedly.
The three keywords are: 'try' (identifies a block of code where exceptions may occur), 'throw' (used to signal the occurrence of an error), and 'catch' (defines the block that handles the specific exception thrown).
In C++, 'throw' is an operator used to raise an exception. Unlike Java, C++ does not use a 'throws' keyword in function signatures. C++ previously used 'throw()' specifications, but these are now deprecated in favor of the 'noexcept' keyword.
Stack unwinding is the process where the runtime system removes function frames from the stack until a matching 'catch' block is found. During this process, destructors are called for all local objects in the exited scopes, ensuring proper resource cleanup.
A namespace is a declarative region that provides a scope to the identifiers (names of types, functions, variables, etc.) inside it. Namespaces are used to organize code into logical groups and to prevent name collisions between different libraries.
The 'std' namespace is the standard namespace in C++ that contains all the built-in library features, including STL containers, algorithms, and I/O streams like cin and cout. It is used to prevent naming conflicts between user-defined functions and the standard library.
The 'using' directive (e.g., using namespace std;) allows all names from a specific namespace to be used without the namespace prefix. While convenient, it is often discouraged in large projects or header files because it can lead to naming collisions between different libraries.
An inline function is a function that the compiler attempts to expand at the point of call rather than performing a standard function call. This reduces the overhead of function calling, such as pushing arguments onto the stack, making it efficient for small, frequently used functions.
Inline functions are handled by the compiler, follow scope rules, and provide type safety, whereas macros are simple text substitutions handled by the preprocessor. Unlike macros, inline functions can be debugged easily and do not suffer from unexpected side effects during evaluation.
A lambda expression is an anonymous function that can be defined directly within a piece of code. It allows for capturing variables from the surrounding scope and is frequently used as a local predicate or callback for STL algorithms like sort or find_if.
The 'auto' keyword allows the compiler to automatically deduce the data type of a variable from its initializer. This is particularly useful for complex types like STL iterators, as it makes the code cleaner and more maintainable without sacrificing type safety.
The 'decltype' keyword is used to inspect the type of an expression or variable at compile-time. Unlike 'auto', which deduces the type based on an assignment, 'decltype' extracts the exact type including references and const qualifiers, which is helpful in template meta-programming.
Move semantics allow resources (like dynamically allocated memory) to be transferred from one object to another instead of being copied. This significantly improves performance, especially when dealing with temporary objects, by avoiding expensive deep copies and unnecessary memory allocations.
An rvalue reference (Type&&) is a type of reference that can only bind to temporary objects (rvalues). It is the backbone of move semantics and perfect forwarding, allowing developers to distinguish between permanent objects and those that are about to be destroyed.
Perfect forwarding is a technique using templates and rvalue references (std::forward) to pass arguments to another function while preserving their original value category (lvalue or rvalue). This ensures that move semantics are used correctly throughout a chain of function calls.
Variadic templates are templates that can accept any number of arguments of any type. They use the ellipsis (...) syntax and are used to implement features like std::tuple or functions that take a variable number of parameters without using unsafe C-style varargs.
The 'constexpr' keyword specifies that the value of a variable or the return value of a function can be evaluated at compile-time. This allows for better performance by shifting calculations from runtime to the compiler and enables the use of results in contexts requiring constants.
The 'explicit' keyword is used in constructor declarations to prevent the compiler from performing unintended implicit type conversions. It ensures that the constructor can only be used for direct initialization, making the code safer and more predictable by avoiding hidden conversions.
The 'mutable' keyword allows a specific member variable of a class to be modified even if the object is declared as const or within a const member function. It is typically used for internal state like mutexes or cache values that don't change the logical state.
The 'volatile' keyword tells the compiler that a variable's value may be changed by something outside the program's control (like a hardware register or a different thread). It prevents the compiler from applying optimizations that might skip reading the variable's value multiple times.
'const' implies that a value is read-only after initialization, but that initialization can happen at runtime. 'constexpr' is a stricter requirement that forces the value to be determined at compile-time, allowing it to be used for array sizes or template parameters.
Static libraries are linked directly into the final executable at compile-time, resulting in a larger file but no external dependencies. Dynamic libraries (DLLs/Shared Objects) are loaded at runtime, allowing multiple programs to share the same library code and reducing overall disk usage.
Name mangling is a technique used by C++ compilers to encode additional information (like function parameters and namespaces) into a unique function name. This is necessary to support function overloading, as it allows the linker to distinguish between functions with the same name.
The extern "C" directive is used to tell the C++ compiler to disable name mangling for a specific block of code. This allows C++ code to correctly link with functions written in C or libraries that expect C-style linkage convention.
Multiple inheritance is a feature where a class can inherit members and behaviors from more than one base class. While powerful, it can lead to complexity and the 'Diamond Problem', which is solved in C++ using virtual inheritance to ensure shared base classes.
Additional Important Questions10
The Rule of Three states if you define a destructor, copy constructor, or copy assignment operator, you likely need all three. Rule of Five adds move constructor and move assignment. Rule of Zero suggests using smart pointers to avoid defining any of them manually.
Copy elision is a compiler optimization technique that eliminates unnecessary copying of objects. The compiler can construct an object directly in its final destination instead of creating a temporary and then copying it, significantly improving performance even if the copy constructor has side effects.
RVO is a specific form of copy elision where the compiler avoids copying a function's return value into the calling scope. It allows the function to operate directly on the memory where the return value will reside, effectively making 'return-by-value' as efficient as 'pass-by-reference'.
C++11 was a major overhaul adding auto, lambdas, and move semantics. C++14 brought minor refinements. C++17 added structured bindings and std::optional. C++20 is another massive update introducing concepts, ranges, coroutines, and modules, modernizing the language's core capabilities significantly.
Thread safety is a property of code indicating that it functions correctly when accessed by multiple threads simultaneously. It ensures that shared data is protected from race conditions through synchronization mechanisms like mutexes, atomic variables, or by ensuring data is immutable.
A mutex (mutual exclusion) is a synchronization primitive used to prevent multiple threads from accessing a shared resource at the same time. A lock (like std::lock_guard) is an RAII wrapper that automatically acquires and releases a mutex to ensure thread-safe execution.
In a struct, each member has its own dedicated memory location, and the total size is the sum of members. In a union, all members share the same memory location, meaning only one member can be stored at a time, resulting in the size of the largest member.
The 'sizeof' operator is a compile-time operator used to determine the size, in bytes, of a data type or an object. It is essential for memory allocation, pointer arithmetic, and understanding the memory footprint of different structures on a specific architecture.
Type casting converts one data type into another. C++ supports C-style casts and four specific C++ casts: static_cast (standard conversions), dynamic_cast (safe downcasting), const_cast (adding/removing const), and reinterpret_cast (low-level reinterpretation), each providing different levels of safety and intent.
static_cast is performed at compile-time and is used for well-defined conversions between related types. dynamic_cast is performed at runtime and is used for safe downcasting in inheritance hierarchies, returning nullptr if the cast is invalid, which requires the base class to have at least one virtual function.