Unlock C++ Polymorphism, malloc, and Memory Management Secrets for Interview Mastery
This comprehensive guide delves into C++ polymorphism, dynamic memory allocation with malloc, Linux memory management, concurrency techniques, and database fundamentals, providing high‑frequency interview questions and practical code examples to help candidates excel in competitive tech recruitment.
Part1 C++多态:让代码 “七十二变”
1.1 C++多态
Polymorphism is a core concept of object‑oriented programming, allowing the same interface to have different implementations. For example, a Shape base class defines a draw method, and derived classes like Circle and Rectangle override it, enabling code to call draw on a Shape* pointer and get the appropriate behavior at runtime.
In C++, polymorphism is achieved through virtual functions and dynamic binding; Java uses method overriding and runtime binding. Polymorphism is widely used in framework design, plugin systems, and strategy patterns.
Key points of implementation:
Virtual functions declared with the virtual keyword.
Each class with virtual functions has a virtual table (vtable) storing function pointers.
Dynamic binding looks up the correct function in the vtable at runtime.
Dynamic linking resolves the actual function address during execution.
While virtual functions add memory and time overhead (a vptr per object and vtable lookup), the flexibility they provide usually outweighs the cost.
1.2 How Polymorphism Solves Code Reuse
Without polymorphism, each shape would need separate drawing functions, leading to duplicated code. By introducing a Shape base class with a virtual draw method, new shapes can be added without modifying existing drawing logic.
class Circle { public: void drawCircle() { std::cout << "Drawing a circle" << std::endl; } };
class Rectangle { public: void drawRectangle() { std::cout << "Drawing a rectangle" << std::endl; } };
class Triangle { public: void drawTriangle() { std::cout << "Drawing a triangle" << std::endl; } };
int main() { Circle c; Rectangle r; Triangle t; c.drawCircle(); r.drawRectangle(); t.drawTriangle(); return 0; }Using polymorphism:
class Shape { public: virtual void draw() = 0; };
class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle" << std::endl; } };
class Rectangle : public Shape { public: void draw() override { std::cout << "Drawing a rectangle" << std::endl; } };
class Triangle : public Shape { public: void draw() override { std::cout << "Drawing a triangle" << std::endl; } };
void drawShapes(Shape* shapes[], int count) { for (int i = 0; i < count; ++i) shapes[i]->draw(); }
int main() { Circle c; Rectangle r; Triangle t; Shape* shapes[] = { &c, &r, &t }; drawShapes(shapes, 3); return 0; }This eliminates duplicated drawing code and makes the system extensible.
1.3 Implementation Principles of Polymorphism
C++ implements polymorphism through:
(1) Overloading
Multiple functions with the same name but different parameters are resolved at compile time.
void add(int a, int b) { ... }
void add(double a, double b) { ... }(2) Overriding
Derived classes override virtual base‑class functions; the call is resolved at runtime via a base‑class pointer or reference.
class Base { public: virtual void func() { ... } };
class Derived : public Base { public: void func() override { ... } };
Base* b = new Derived();
b->func(); // Calls Derived::func()(3) Compile‑time polymorphism (templates)
template <typename T>
void func(T t) { ... }
func(1); // Calls func<int>
func(3.2); // Calls func<double>(4) Conditional compilation
#ifdef _WIN32
void func() { ... } // Windows version
#elif __linux__
void func() { ... } // Linux version
#endifOverall, runtime polymorphism via virtual functions provides the most flexibility.
Part2 malloc:动态内存分配的魔法棒
2.1 malloc技术
In C and C++, malloc allocates memory at runtime. Its prototype is void* malloc(size_t size). Example allocating space for 10 integers:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = (int*)malloc(10 * sizeof(int));
if (arr == NULL) { printf("Allocation failed
"); return 1; }
for (int i = 0; i < 10; ++i) arr[i] = i;
for (int i = 0; i < 10; ++i) printf("%d ", arr[i]);
free(arr);
return 0;
}Key points: always check for NULL, and free the memory with free to avoid leaks.
2.2 Malloc函数
(1) 数据结构
A simple block structure can be used:
typedef struct s_block *t_block;
struct s_block {
size_t size; // data size
t_block next; // next block
int free; // free flag
int padding; // alignment padding
char data[1]; // start of data area
};(2) 查找合适的 block
First‑fit algorithm scans from the head and returns the first block large enough:
t_block find_block(t_block *last, size_t size) {
t_block b = first_block;
while (b && b->size >= size) {
*last = b;
b = b->next;
}
return b;
}(3) 开辟新 block
#define BLOCK_SIZE 24
t_block extend_heap(t_block *last, size_t s) {
t_block b = sbrk(0);
if (sbrk(BLOCK_SIZE + s) == (void*)-1) return NULL;
b->size = s;
b->next = NULL;
if (last) (*last)->next = b;
b->free = 0;
return b;
}(4) 分裂 block
void split_block(t_block b, size_t s) {
t_block newb = (t_block)(b->data + s);
newb->size = b->size - s - BLOCK_SIZE;
newb->next = b->next;
newb->free = 1;
b->size = s;
b->next = newb;
}2.3 Malloc函数的实现原理
(1) 空闲链表机制
malloc maintains a free‑list of blocks. When a request arrives, it searches for a suitable block; if the block is larger than needed, it splits it, allocating the required part and returning the remainder to the free‑list.
(2) 虚拟内存与物理内存
Modern OSes use virtual memory. A process works with virtual addresses; the MMU translates them to physical addresses using page tables. If a page is not resident, a page‑fault triggers loading from swap or disk.
(3) 页与地址构成
Pages are typically 4 KB. Virtual addresses consist of a page number and offset; the offset is unchanged, while the page number is translated via the page table.
(4) Linux进程级内存管理
Linux divides a process’s address space into sections: code, data, BSS, heap (grows upward), mmap area, and stack (grows downward). The heap grows from low to high addresses, and the break pointer marks the current end of the heap.
Part3 内存管理:守护程序的 “内存管家”
3.1 Linux内存管理
Memory management allocates, recycles, and optimizes memory resources. Static allocation occurs at compile time (global/static variables). Dynamic allocation uses malloc, new, etc., but requires careful handling to avoid leaks and fragmentation.
Linux uses a page‑frame allocator for physical memory and a virtual‑memory subsystem with page tables to map virtual addresses to physical frames.
3.2 物理内存与虚拟内存
Physical memory consists of fixed‑size page frames (usually 4 KB). Virtual memory gives each process an independent address space, with the MMU translating virtual pages to physical frames via page tables. Page faults load missing pages from swap or disk.
3.3 分页:虚拟内存的基石
Paging divides both virtual and physical memory into fixed‑size pages. The CPU’s CR0.PG flag enables paging. Linear addresses are split into a page number and offset; the page number indexes the page table, and the offset is added to the physical frame address.
3.4 交换空间:内存不足时的后盾
When physical memory is scarce, the kernel swaps out rarely used pages to a designated swap area on disk. Swapped pages are brought back on demand, but excessive swapping degrades performance.
3.5 内存映射:高效 I/O 的秘诀
Memory‑mapped I/O ( mmap) maps a file or device into a process’s address space, allowing direct memory access without explicit read / write calls. The mapping is created in the virtual address space, and actual data is loaded on first access via a page‑fault.
Process calls mmap to reserve a virtual region.
Kernel creates a vm_area_struct and links it to the file’s struct file.
On first access, a page‑fault triggers loading the file data into a physical page and updating the page table.
Part4 并发编程:开启程序高效运行的大门
4.1 并发编程技术
Concurrency lets multiple tasks run simultaneously on multi‑core CPUs, improving throughput. Common challenges include data races, deadlocks, and priority inversion. Synchronization primitives such as mutexes, read‑write locks, and atomic operations (e.g., std::atomic) protect shared data.
Thread pools reuse a fixed set of threads to avoid the overhead of frequent thread creation. Concurrent containers like ConcurrentHashMap provide thread‑safe access without external locking. Message queues (Kafka, RabbitMQ) decouple producers and consumers for asynchronous processing.
4.2 什么是无锁编程
Lock‑free (non‑blocking) algorithms achieve synchronization without blocking threads, avoiding deadlocks, lock convoy, and priority inversion. Levels of progress guarantees:
Wait‑free : every thread completes in a bounded number of steps.
Lock‑free : at least one thread makes progress.
Obstruction‑free : a thread makes progress if it runs in isolation.
4.3 为什么要无锁?
Lock‑free designs can offer higher performance and eliminate classic lock‑related bugs such as deadlock and convoying, especially in high‑throughput systems.
4.4 如何无锁?
Modern CPUs provide atomic read‑modify‑write primitives. On x86/x64 this is typically compare‑and‑swap (CAS); on ARM/PowerPC it is load‑link/store‑conditional (LL/SC). GCC offers built‑ins like __sync_bool_compare_and_swap and __sync_fetch_and_add to perform atomic operations.
bool CAS(int* addr, int expected, int desired) {
return __sync_bool_compare_and_swap(addr, expected, desired);
}
int old = __sync_fetch_and_add(&counter, 1); // atomically incrementPart5 数据库:数据世界的坚固堡垒
A database is a structured collection of data stored for long‑term use and sharing. DBMS software (MySQL, Oracle, PostgreSQL, etc.) provides creation, retrieval, update, and management capabilities, ensuring consistency, security, and concurrency control.
Relational databases model data as tables with rows and columns, using SQL for powerful queries and transactions. NoSQL databases (MongoDB, Redis) handle unstructured or semi‑structured data with flexible schemas, offering high scalability and performance for specific workloads.
Database design follows three stages: requirement analysis, conceptual design using ER diagrams, logical design (mapping ER to relational schema), and physical design (indexing, storage layout). Performance tuning includes proper indexing, query optimization, and configuration tuning (buffer pools, connection limits, etc.).
Databases power e‑commerce platforms, social networks, financial systems, and countless other applications, forming the backbone of modern information systems.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
