1. Static Functions, Const Functions, and Virtual Functions
- Static Functions Static functions cannot be defined as virtual functions. They belong to the class itself, not to instances of the class, and do not have a
<span>this</span>pointer. Virtual function calls depend on the object’s virtual function table, requiring the<span>this</span>pointer for location, while static functions can be called directly by the class name without relying on object instantiation, which contradicts the polymorphic nature of virtual functions based on objects. - Const Functions Const functions can be defined as virtual functions. A const function is a function that adds the
<span>const</span>keyword at the end of its declaration and definition, promising not to modify the object’s state. When called as a virtual function through a const reference or const pointer of the base class, it will call the corresponding virtual function based on the actual type of the object, achieving dynamic binding.
2. Differences Between Function Pointers and Functors
- Definition A function pointer is a pointer variable that points to a function, such as
<span>int (*funcPtr)(int, int);</span>; a functor is an object of a class or struct that overloads the<span>()</span>operator, essentially being an object but used like a function, for example,<span>struct Adder { int operator()(int a, int b) { return a + b; } };</span>. - State Preservation Function pointers have no state and only point to a fixed function; functors can preserve state through member variables, with different objects having different states.
- Performance Function pointer calls incur indirect overhead, requiring a pointer lookup for the function address; functor calls can be inlined by the compiler under suitable conditions, resulting in better performance.
- Flexibility Function pointers can only point to functions with specific signatures, offering low flexibility; functors can achieve more flexible functionality through templates and inheritance, and can also be overloaded based on parameter types.
3. C++ Memory Segmentation
- Stack Segment Automatically allocated and released by the operating system, storing local variables, function call information, etc. Allocation and release are fast, but space is limited.
- Heap Segment Must be manually allocated and released by the programmer, using
<span>new</span>for allocation and<span>delete</span>for release in C++; in C, the corresponding functions are<span>malloc</span>and<span>free</span>. The memory space is large, but allocation and release are slower. - Global/Static Segment Stores global and static variables, with memory allocated at program startup and released at program termination.
- Constant Segment Stores constant data, such as string constants, which cannot be modified during program execution.
- Code Segment Stores the executable code of the program, has read-only attributes, and can be shared by multiple processes.
4. Differences Between Heap and Stack
- Memory Allocation Method
- Stack: Automatically allocated and released by the operating system. When a function is called, local variables and call information are automatically pushed; when the function returns, they are automatically popped without programmer intervention.
- Heap: Manually allocated and released by the programmer. In C++, use
<span>new</span>for allocation and<span>delete</span>for release; in C, use<span>malloc</span>for allocation and<span>free</span>for release.
- Stack: Generally smaller space, preset by the operating system. For example, under Windows, it is usually 2M (also 1M, configurable at compile time), used for short-term storage needs like local variables.
- Heap: Relatively larger space, theoretically can use most of the system memory, limited by virtual memory, such as 4G on a 32-bit system, with more flexible allocation.
- Stack: Fast allocation and release, managed automatically by the operating system, with a simple mechanism, operating under fixed rules, resulting in high efficiency.
- Heap: Slow allocation and release, management requires maintaining a free memory linked list, searching for suitable memory blocks during allocation, and consolidating back into the list during release, which can lead to fragmentation and lower efficiency.
- Stack: Stores local variables, parameter passing, return addresses, and temporary data, with a lifecycle closely related to function calls, created at the start of the function and destroyed at the end.
- Heap: Stores dynamically allocated objects and data, such as large data structures and object instances, with a lifecycle controlled manually by the programmer, independent of function calls.
5. Class Object Storage Space and Size of Empty Classes
- Composition of Class Object Storage Space
- Non-static Data Members: Class objects contain all non-static data members, which are stored contiguously in memory according to their declaration order. However, actual storage may be affected by memory alignment rules. For example, in
<span>struct { char c; int i; }</span>, since<span>int</span>types are usually aligned to 4 bytes, and<span>char</span>types occupy 1 byte, there may be 3 bytes of padding after<span>char</span>to ensure that the storage address of<span>int</span>meets alignment requirements, thus improving CPU access efficiency. - Virtual Function Pointer (vptr): If a class contains virtual functions, the object will have a virtual function pointer (vptr). In a 64-bit system, this pointer usually occupies 8 bytes and is generally located at the start of the object. The vptr points to the virtual function table, enabling dynamic polymorphism, allowing the program to call the corresponding virtual function based on the actual type of the object at runtime.
- Base Class Subobject: When a class has an inheritance relationship, the object will contain base class subobjects. Base class members are prioritized in the object’s memory layout, and in multiple inheritance cases, base class subobjects are arranged in the order of declaration. However, multiple inheritance may lead to the “diamond inheritance” problem, which requires virtual inheritance to resolve the issue of duplicate inheritance of base class members, avoiding data redundancy and ambiguity.
- Memory Alignment Padding: To meet CPU access efficiency requirements, the compiler will insert blank bytes for memory alignment padding. The alignment number is usually a multiple of the size of the largest basic data type among the object members. Although this padding operation does not store valid data, it is crucial for enhancing memory access performance.
- As Base Class: Under certain conditions, when an empty class is inherited by other classes, it may trigger Empty Base Class Optimization (EBCO), where the base class part in the derived class object may not occupy additional space, effectively optimizing its size to 0 bytes, thus saving memory.
- When Containing Virtual Functions: If an empty class contains virtual functions, its object size is fixed to the size of the virtual pointer, which is 8 bytes in a 64-bit system. This is because the virtual function mechanism relies on the virtual function pointer and the virtual function table; even if the class is empty, as long as there are virtual functions, the virtual function pointer must be stored to support dynamic polymorphism.
- General Case: The size of an empty class (a class without any non-static data members) is at least 1 byte. This is because each object in memory needs a unique address identifier, and even if the class has no actual data, it still needs to occupy some space to distinguish different object instances.
6. Differences Between Global Variables and Local Variables
- Scope Global variables have a scope throughout the entire program, accessible from the point of definition until the program ends; local variables have a scope limited to the code block in which they are defined, such as within functions or loops, and cannot be accessed outside of it.
- Lifetime Global variables exist for the duration of the program, created at startup and destroyed at termination; local variables are created when the code block is executed and destroyed at the end of the block.
- Storage Location Global variables are stored in the global data area (static storage area); local variables are stored on the stack, with stack space allocated during function calls and released upon return.
- Initialization Global variables are automatically initialized to default values (numeric 0, pointer
<span>nullptr</span>); local variables, when uninitialized, have undefined values, leading to undefined behavior if used.
7. Usage of Pointers and <span>const</span>
- Constant Pointer (Bottom-level
<span>const</span>) The object pointed to by the pointer is constant and cannot be modified through this pointer, but the pointer itself can point to other objects, such as<span>const int* p;</span>. - Pointer Constant (Top-level
<span>const</span>) The pointer itself is constant and cannot point to other objects after initialization, but the value of the object it points to can be modified, such as<span>int* const p;</span>. - Constant Pointer to Constant Object Both the pointer itself and the object it points to are constant, meaning neither the pointer can be modified nor the value of the object can be changed, such as
<span>const int* const p;</span>.
8. Differences Between Inline Functions and Macros
- Processing Stage: Macros are processed during the preprocessing stage for text replacement; inline functions are processed during the compilation stage.
- Type Checking: Macros have no type checking, only simple replacement, which can easily lead to type mismatches; inline functions have type checking, enhancing type safety.
- Debugging Information: Macros have no related information in the code after preprocessing, making debugging difficult; inline functions are visible during debugging.
- Scope: Macros have no scope limitations, being valid throughout the entire file after definition; inline functions have scope limitations.
- Code Bloat: Macros may cause code bloat due to simple text replacement; whether an inline function is actually inlined is determined by the compiler, which may not necessarily cause code bloat.
9. Keywords for Constructors
<span>explicit</span>: Used to modify single-parameter constructors to prevent implicit type conversion.<span><span><span>class</span></span><span><span>MyClass</span></span><span>{</span></span><span><span>public</span></span><span>:</span><span> </span><span><span><span>explicit</span></span><span><span>MyClass</span></span><span><span>(</span><span><span>int</span></span><span> num)</span></span><span> : </span><span><span>data</span></span><span><span>(num)</span></span></span><span>{}</span><span><span>private</span></span><span>:</span><span> </span><span><span>int</span></span><span> data;</span><span>};</span><span><span>// MyClass obj = 10; // Compilation error, implicit conversion is prohibited</span></span><span><span>MyClass </span><span><span>obj</span></span><span><span>(</span><span><span>10</span></span><span>)</span></span></span><span>; </span><span><span>// Correct</span></span><span>default</span>: Used to explicitly request the compiler to generate a default constructor.<span><span><span>class</span></span><span><span>MyClass</span></span><span>{</span></span><span><span>public</span></span><span>:</span><span> MyClass() = </span><span><span>default</span></span><span>;</span><span>};</span><span>delete</span>: Used to explicitly delete the default constructor, preventing objects from being default constructed.<span><span><span>class</span></span><span><span>MyClass</span></span><span>{</span></span><span><span>public</span></span><span>:</span><span> MyClass() = </span><span><span>delete</span></span><span>;</span><span>};</span>
10. Access and Inheritance Permissions of <span>public</span>, <span>protected</span>, and <span>private</span>
- Class Member Access Permissions
<span>public</span>: Members can be accessed from outside the class.<span>private</span>: Members can only be accessed within the class, not from outside.<span>protected</span>: Members can be accessed within the class and derived classes, but not from outside.
<span>public</span>Inheritance: Base class<span>public</span>members remain<span>public</span>in the derived class,<span>protected</span>members remain<span>protected</span>, and<span>private</span>members cannot be accessed.<span>private</span>Inheritance: Base class<span>public</span>and<span>protected</span>members become<span>private</span>in the derived class, and<span>private</span>members cannot be accessed.<span>protected</span>Inheritance: Base class<span>public</span>and<span>protected</span>members become<span>protected</span>in the derived class, and<span>private</span>members cannot be accessed.
11. Usage and Differences of <span>define</span>, <span>const</span>, <span>typedef</span>, and <span>inline</span>
- Usage
<span>define</span>: A preprocessor directive that defines a macro, replacing the macro name with the defined value before compilation, such as<span>#define PI 3.14159</span>,<span>#define MAX(a, b) ((a) > (b) ? (a) : (b))</span>.<span>const</span>: Defines a constant whose value cannot be modified, applicable to variables, pointers, function parameters, etc., such as<span>const int num = 10;</span>,<span>const int* ptr = #</span>,<span>void func(const int& param) {}</span>.<span>typedef</span>: Defines a new name for an existing type, facilitating code writing and readability, such as<span>typedef int Integer;</span>,<span>typedef void(*FuncPtr)(int);</span>.<span>inline</span>: Defines an inline function, where the function body code is inserted at the call site, reducing overhead, such as<span>inline int add(int a, int b) { return a + b; }</span>.
- Calling Method: Regular function calls incur overhead for parameter pushing, etc.; inline function calls directly insert the function body code, reducing overhead.
- Compiler Processing:
<span>inline</span>only requests inlining; the compiler may not process complex functions. - Functionality:
<span>typedef</span>defines type aliases, while<span>define</span>defines macros, with the former focusing on types and the latter on text replacement. - Syntax:
<span>typedef</span>is a keyword with strict syntax;<span>define</span>is a preprocessor directive with flexible syntax. - Type Checking:
<span>define</span>has no type checking, while constants defined with<span>const</span>have types, with the compiler checking them, enhancing safety. - Scope:
<span>define</span>has no concept of scope, being valid throughout the file after definition; constants defined with<span>const</span>have scope limitations. - Debugging Information:
<span>define</span>is replaced at compile time, making it invisible during debugging; constants defined with<span>const</span>are visible during debugging with their definitions and values.
12. Lambda Functions
- Syntax Structure The form is
<span>[capture list](parameter list) -> return type {function body}</span>. The capture list specifies the external variables that can be accessed, while the parameter list and return type are optional. - Capture List Can be empty, not capturing external variables; can also use
<span>&</span>or<span>=</span>to capture external variables by reference or value, such as<span>[&]</span>captures all by reference, and<span>[=]</span>captures all by value. - Parameter List Similar to regular functions, can contain multiple parameters separated by commas.
- Return Type Can be omitted, with the compiler automatically inferring it based on the return value of the function body.
- Function Body Contains the specific implementation code.
- Advantages
- Simplicity: Can be defined directly where needed, without needing a separate function definition.
- Flexibility: Can capture external variables, making it convenient to use within the function body.
- Can Be Passed as Parameters: Can be passed to other functions, enabling callback functionalities.
13. Operator Overloading
- Definition: In C++, new behaviors can be defined for existing operators, making them applicable to custom types, such as overloading the
<span>+</span>operator to implement addition for custom class objects. - Syntax: Return type
<span>operator</span>operator (parameter list). For example:<span><span><span>class</span></span><span><span>MyClass</span></span><span>{</span></span><span><span>public</span></span><span>:</span><span> </span><span><span>int</span></span><span> data;</span><span> MyClass </span><span><span>operator</span></span><span>+(</span><span><span>const</span></span><span> MyClass& other) {</span><span> MyClass result;</span><span> result.data = </span><span><span>this</span></span><span>->data + other.data;</span><span> </span><span><span>return</span></span><span> result;</span><span> }</span><span>};</span> - Notes: Cannot change operator precedence and associativity; cannot create new operators; some operators like
<span>::</span>,<span>.*</span>, and<span>?:</span>cannot be overloaded.
14. What Happens to the Stack When Calling Member Variables with the this Pointer?
When using the <span>this</span> pointer to call member variables, the main change in the stack is the use of the function call stack.
- Function Call Stack: When a member function is called, a stack frame is allocated for that function on the stack. The stack frame contains the function’s local variables, return address, and the
<span>this</span>pointer.<span>this</span>points to the object that called the member function. - Accessing Member Variables: When using the
<span>this</span>pointer to access member variables within the member function, it actually accesses the member variables through the memory address of the object pointed to by the<span>this</span>pointer. This process does not have additional effects on the stack; it simply reads or writes data from the object’s memory space.
For example:
class MyClass { public: int data; void setData(int value) { this->data = value; // Accessing member variable through this pointer } }; int main() { MyClass obj; obj.setData(10); return 0; }
When calling <span>obj.setData(10)</span>, a stack frame is created on the stack for the <span>setData</span> function, which includes the <span>this</span> pointer pointing to the <span>obj</span> object. Inside the <span>setData</span> function, the <span>this</span> pointer is used to access the <span>data</span> member variable, setting its value to 10.
15. Differences Between <span>_stdcall</span> and <span>_cdecl</span>
- Parameter Passing Order: Both pass parameters from right to left onto the stack.
- Stack Cleanup Method:
<span>_stdcall</span>cleans up the stack parameters by the called function, commonly used in Windows API functions;<span>_cdecl</span>cleans up the stack parameters by the calling function, supporting variable argument functions.- Name Mangling:
<span>_stdcall</span>mangles function names by prefixing with an underscore and suffixing with<span>@</span>and the number of bytes of parameters;<span>_cdecl</span>typically only prefixes function names with an underscore.