Roadmap

bird

Even when it is work, I still want to find the fun in it.

Knowledge Elements

Practical knowledge points that show up often in real development, not limited to one language.

Linux Commands I May Forget

System Management

# Print the current directory
pwd
# Show disk usage
df -h
# Show file or directory size
du -sh file_name
# Show memory usage
free -h
# Show logged-in users
who

Permissions

# Show file permissions
ls -l file_name
# Change file permissions
chmod 755 file_name

Networking

# Show network interfaces
ifconfig
# Test connectivity
ping host_name
# Download a file
wget url

Processes

# List processes
ps aux
# Monitor processes in real time
top
# Kill a process
kill pid
# Force-kill a process
kill -9 pid

Archive

# Create a compressed archive
tar -zcvf archive.tar.gz dir_name
# Extract an archive
tar -zxvf archive.tar.gz
Design Ideas Behind Message Queues

Message queues are a common engineering solution for asynchronous communication, system decoupling, and traffic buffering. The core idea is to put a durable buffer between producers and consumers so each side can evolve and scale independently.

Suitable Scenarios

  • Asynchronous processing: producers submit tasks and continue without waiting for consumers to finish.
  • System decoupling: services communicate through messages instead of direct calls, reducing dependency pressure.
  • Traffic peak shaving: sudden bursts are stored first, then processed at a pace downstream systems can handle.

Common Products

  • ActiveMQ: an open-source Java message broker.
  • RabbitMQ: an AMQP-based broker written in Erlang.
  • Kafka: a distributed log system designed for high-throughput streaming.
  • RocketMQ: a distributed messaging platform with strong consistency and high performance.
  • Pulsar: a cloud-native streaming platform with multi-tenancy and cross-region replication.

Core Design

A message queue normally has three parts:

  • Producer: creates messages and decides which queue or partition receives them.
  • Queue cluster: stores, filters, and dispatches messages. Storage design usually determines throughput and reliability.
  • Consumer: reads messages and processes them, commonly through push or pull models.

Data Organization

Different products use different storage models. Kafka uses an append-only log for high throughput. RocketMQ and Pulsar focus more on consistency, availability, and distributed storage control.

Consumer Model

Consumer models affect ordering and delivery guarantees. Kafka keeps ordering inside a partition, while other systems may support different subscription and consumer-group models.

Summary

Message queues are not just middleware. They encode several practical design ideas: buffering, decoupling, durability, ordering, retry, and backpressure.

Scattered topics that are useful but not always held in muscle memory.

Differences Between C and C++

Differences Between C and C++

  1. Language model
  • C is procedural and focuses on functions and data structures.
  • C++ extends C and supports object-oriented programming, including classes, inheritance, and polymorphism.
  1. Object-oriented programming
  • C has no built-in class or object model.
  • C++ supports encapsulation, inheritance, polymorphism, constructors, destructors, and access control.
  1. Templates
  • C has no template mechanism.
  • C++ supports templates, which are used to write generic code.
  1. Standard library
  • C mainly provides headers such as stdio.h and stdlib.h.
  • C++ provides higher-level abstractions such as iostream, vector, string, algorithms, and containers.
  1. Memory and resource management
  • C relies mainly on manual memory management through malloc and free.
  • C++ supports RAII, constructors/destructors, smart pointers, and deterministic cleanup.
Differences Between Pointers and References

Differences Between Pointers and References

  1. Definition
  • A pointer stores the address of another object and can be reassigned.
  • A reference is an alias for an existing object and cannot be rebound after initialization.
  1. Syntax
int a = 10;
int *p = &a;
int &r = a;
  1. Nullability
  • A pointer can be nullptr.
  • A reference should always refer to a valid object.
  1. Reassignment
  • A pointer can point to different objects during its lifetime.
  • A reference always refers to the object it was initialized with.
  1. Usage
  • Use pointers when null, ownership, or reseating is meaningful.
  • Use references when a valid object is required and no ownership transfer is implied.
Differences Between struct and class

Differences Between struct and class

  1. Default access
  • struct members are public by default.
  • class members are private by default.
  1. Default inheritance
  • struct inherits publicly by default.
  • class inherits privately by default.
  1. Typical usage
  • struct is often used for simple aggregate data.
  • class is often used for objects with invariants, encapsulation, and behavior.
  1. Capability
  • In C++, struct and class can both have member functions, constructors, destructors, inheritance, and templates.
  • The practical difference is convention and default access control, not capability.
struct Point {
    int x;
    int y;
};

class Counter {
public:
    void inc() { ++value; }
private:
    int value = 0;
};
Struct Alignment, sizeof, and strlen

In C and C++, structure layout is affected by alignment. The compiler may insert padding bytes between members so each member is placed at an address suitable for its type.

Struct Alignment

For example, an int may require 4-byte alignment. If a char is followed by an int, the compiler may insert padding between them.

struct Example {
    char a;
    int b;
    short c;
};

The size of this structure is often larger than the sum of its members because of padding.

Why Alignment Exists

Aligned memory access is usually faster and may be required by some architectures. Padding trades a small amount of space for safer and faster access.

sizeof

sizeof is evaluated by the compiler and returns the storage size of a type or object, including padding and the null terminator for character arrays.

char s[] = "abc";
sizeof(s); // 4

strlen

strlen counts characters before the first '\0' at runtime. It only works for null-terminated C strings.

strlen(s); // 3

Summary

  • sizeof measures storage size.
  • strlen measures string length before '\0'.
  • struct size may include padding.
The Three Main Features of OOP

The three main features of object-oriented programming are encapsulation, inheritance, and polymorphism.

  1. Encapsulation
  • Encapsulation binds data and operations together and hides internal implementation details.
  • It improves maintainability and protects object invariants.
  1. Inheritance
  • Inheritance allows a derived class to reuse and extend the behavior of a base class.
  • It can reduce duplication, but should be used carefully to avoid fragile hierarchies.
  1. Polymorphism
  • Polymorphism lets the same interface dispatch to different behavior depending on the concrete type.
  • In C++, runtime polymorphism is usually implemented through virtual functions.

These features help organize complex systems, but good design still depends on clear ownership, boundaries, and responsibilities.

Class Access Control

C++ provides three main access levels for class members: private, protected, and public.

private

private members can only be accessed by member functions and friends of the same class. They are used to hide implementation details.

class MyClass {
private:
    int value;
public:
    void setValue(int v) { value = v; }
    int getValue() const { return value; }
};

protected

protected members can be accessed by the class itself, friends, and derived classes. They are useful when inheritance needs controlled access to base-class internals.

public

public members are part of the external interface. They can be accessed by any code that can see the object.

Summary

  • Keep data members private by default.
  • Expose behavior through a small public interface.
  • Use protected only when inheritance really needs it.
Constructors, Destructors, Assignment, and Copy Functions

These special member functions control object lifetime, copying, moving, and resource management.

Constructor

A constructor creates an object and initializes its members. It has the same name as the class and no return type.

class MyClass {
public:
    MyClass();
    MyClass(int value);
};

Destructor

A destructor runs when an object is destroyed. It is used to release resources.

class File {
public:
    ~File();
};

Copy Constructor

The copy constructor creates a new object from an existing object.

MyClass(const MyClass& other);

Copy Assignment Operator

The copy assignment operator replaces the state of an existing object with another object’s state.

MyClass& operator=(const MyClass& other);

Move Constructor and Move Assignment

Move operations transfer resources from temporary objects instead of copying them.

MyClass(MyClass&& other) noexcept;
MyClass& operator=(MyClass&& other) noexcept;

Rule of Three/Five/Zero

  • If a class manually manages resources, it may need destructor, copy constructor, and copy assignment.
  • In modern C++, prefer the Rule of Zero: use standard library types and let them manage resources.
Copy Constructor vs Move Constructor

Both copy constructors and move constructors initialize a new object from another object. The difference is how resources are handled.

Copy Constructor

A copy constructor duplicates the source object’s state.

MyClass(const MyClass& other);

It is used when:

  • an object is initialized from another lvalue;
  • an object is passed by value;
  • an object is returned and copy elision does not apply.

Copying can be expensive when the object owns large memory buffers or handles.

Move Constructor

A move constructor transfers resources from a temporary or explicitly moved object.

MyClass(MyClass&& other) noexcept;

It is used with rvalues and std::move. Moving is usually cheaper because it can steal pointers or handles instead of duplicating data.

Key Difference

  • Copying preserves the source object’s value.
  • Moving leaves the source object valid but unspecified.
  • Move constructors should usually be marked noexcept, especially for standard containers.
Memory Areas: Global, Heap, Stack, Constant, Code

C and C++ programs organize memory into several common areas.

Global / Static Area

Stores global variables and static variables. Initialized data is placed in the data segment, while zero-initialized data is usually placed in BSS.

Heap

The heap is used for dynamic allocation through malloc, new, and similar APIs. The programmer or owning abstraction must release the memory.

Stack

The stack stores local variables, function parameters, return addresses, and call frames. It is automatically managed when functions are called and returned.

Constant Area

Stores read-only data such as string literals and some constant objects. Modifying string literals is undefined behavior.

Code Area

Stores executable instructions. It is usually read-only during normal program execution.

Practical Notes

  • Stack allocation is fast but limited in size.
  • Heap allocation is flexible but needs ownership management.
  • RAII and smart pointers reduce manual memory mistakes.
How Virtual Functions Implement Dynamic Polymorphism

In C++, virtual functions implement dynamic polymorphism through a virtual table and a virtual pointer.

  • vtable: a table of function pointers generated for a class with virtual functions.
  • vptr: a hidden pointer stored in each polymorphic object, pointing to the object’s class vtable.

When a virtual function is called through a base pointer or reference, the program uses the object’s vptr to find the correct function implementation at runtime.

This is why a base-class interface can call derived-class behavior without knowing the concrete type at compile time.

Virtual Functions vs Pure Virtual Functions

Virtual Function

A virtual function is declared with the virtual keyword. It can have a default implementation in the base class, and derived classes may override it.

class Base {
public:
    virtual void show();
};

When called through a base pointer or reference, the actual function is selected at runtime.

Pure Virtual Function

A pure virtual function has no required base implementation and is declared with = 0.

class Shape {
public:
    virtual double area() const = 0;
};

A class with at least one pure virtual function is abstract and cannot be instantiated directly.

Difference

  • Virtual functions may provide default behavior.
  • Pure virtual functions define an interface that derived classes must implement.
  • Pure virtual functions are commonly used to model abstract interfaces.
Deep Copy vs Shallow Copy

Deep copy and shallow copy are two different ways to duplicate an object.

Shallow Copy

A shallow copy copies the object’s immediate fields. If those fields contain pointers or references, both objects may still point to the same underlying resource.

struct Buffer {
    char* data;
};

A compiler-generated copy of Buffer copies the pointer value, not the memory it points to.

Deep Copy

A deep copy allocates a new resource and copies the actual content.

class Buffer {
public:
    Buffer(const Buffer& other) {
        data = new char[other.size];
        std::copy(other.data, other.data + other.size, data);
    }
private:
    char* data;
    std::size_t size;
};

Risk

Shallow copying owning pointers can cause double free, dangling pointers, or unexpected shared mutation.

Modern C++ Advice

Prefer standard library types such as std::vector, std::string, and smart pointers. They make copying and ownership clearer.

static, const, extern, volatile

static

  • Inside a function: creates a local variable whose lifetime lasts until program exit.
  • At file scope: gives a variable or function internal linkage.
  • In a class: declares a member shared by all objects of that class.

const

const expresses that a value should not be modified through this name.

  • const int x = 10;
  • void f(const std::string& s);
  • int size() const;

extern

extern declares that a variable or function is defined elsewhere. It is often used to share declarations across translation units.

volatile

volatile tells the compiler that a value may change outside normal program flow, such as through hardware or signal handlers. It prevents some optimizations, but it is not a thread synchronization primitive.

C++ Type Casts

C++ provides four named casts. They are more explicit and safer than C-style casts.

static_cast

Used for well-defined compile-time conversions.

double d = static_cast<double>(10);

It can also be used for upcasts and some downcasts in inheritance hierarchies, but it does not perform runtime checking.

dynamic_cast

Used for checked casts in polymorphic class hierarchies.

Derived* d = dynamic_cast<Derived*>(base);

It returns nullptr for failed pointer casts and throws for failed reference casts.

const_cast

Used to add or remove const or volatile.

const_cast<int*>(ptr);

Removing const and then modifying an originally const object is undefined behavior.

reinterpret_cast

Used for low-level bit reinterpretation. It is powerful and risky, and should be rare in ordinary application code.

Advice

Prefer named casts because they communicate intent and make unsafe conversions easier to notice during review.

Static and Dynamic Polymorphism: Overriding, Overloading, Templates

Static Polymorphism

Static polymorphism is resolved at compile time. C++ commonly implements it through overloading and templates.

Overloading

Overloading means multiple functions share the same name but have different parameter lists.

void print(int);
void print(double);

The compiler selects the right overload from the argument types.

Templates

Templates generate code for different types at compile time.

template <typename T>
T maxValue(T a, T b) {
    return a > b ? a : b;
}

Dynamic Polymorphism

Dynamic polymorphism is resolved at runtime through virtual functions.

class Base {
public:
    virtual void run();
};

Overriding

Overriding means a derived class provides a new implementation of a virtual function declared in the base class.

Summary

  • Overloading and templates are compile-time mechanisms.
  • Overriding through virtual functions is runtime polymorphism.
  • Static polymorphism is often faster; dynamic polymorphism is often more flexible.
Four Smart Pointers and Their Implementations

C++ smart pointers help manage dynamic memory automatically and reduce leaks.

auto_ptr

auto_ptr was introduced in C++98 and is now deprecated. Copying it transfers ownership, which made ordinary value semantics dangerous.

unique_ptr

unique_ptr represents exclusive ownership.

std::unique_ptr<int> p = std::make_unique<int>(42);

It cannot be copied, but it can be moved. Internally, it usually stores a raw pointer and a deleter.

shared_ptr

shared_ptr represents shared ownership. It uses a control block that stores:

  • the reference count;
  • the weak reference count;
  • the deleter and allocator information.

When the strong count reaches zero, the managed object is destroyed.

weak_ptr

weak_ptr observes an object managed by shared_ptr without increasing the strong reference count. It is used to break cycles.

std::weak_ptr<Node> parent;

Use lock() to obtain a temporary shared_ptr if the object still exists.

Advice

  • Use unique_ptr for clear ownership.
  • Use shared_ptr only when shared lifetime is actually needed.
  • Use weak_ptr to avoid ownership cycles.
Rvalue References

Rvalue references were introduced in C++11 to support move semantics and perfect forwarding.

Lvalues and Rvalues

  • Lvalue: has an identifiable location and can usually appear on the left side of assignment.
  • Rvalue: is a temporary value or expression result that usually does not have a persistent identity.

Syntax

int&& x = 10;

Move Semantics

Rvalue references allow resources to be transferred instead of copied.

std::vector<int> a = makeVector();
std::vector<int> b = std::move(a);

After moving, a remains valid but its value is unspecified.

Perfect Forwarding

Forwarding references preserve the value category of function arguments.

template <typename T>
void wrapper(T&& value) {
    target(std::forward<T>(value));
}

Why It Matters

Rvalue references make high-performance resource management possible without forcing manual memory handling.

std::move

std::move is a standard library utility that casts an object to an rvalue reference. It does not move anything by itself.

template <typename T>
typename std::remove_reference<T>::type&& move(T&& t) noexcept;

Usage

#include <utility>
#include <vector>

std::vector<int> a{1, 2, 3};
std::vector<int> b = std::move(a);

The move happens because the move constructor of std::vector is selected after a is converted to an rvalue.

Important Notes

  • std::move is just a cast.
  • The moved-from object must remain valid.
  • Do not use a moved-from object except to destroy it or assign a new value, unless its type documents stronger guarantees.

Practical Rule

Use std::move when you intentionally give up the current value of an object.

C++ Iterators

Iterators are objects used to traverse container elements. They behave like generalized pointers.

Basic Idea

An iterator supports operations such as dereference and increment.

for (auto it = v.begin(); it != v.end(); ++it) {
    std::cout << *it << '\n';
}

Iterator Categories

  • Input iterator: reads values in one pass.
  • Output iterator: writes values in one pass.
  • Forward iterator: moves forward and can be reused.
  • Bidirectional iterator: moves forward and backward.
  • Random access iterator: supports indexing and constant-time jumps.

Iterator Invalidation

Iterators can become invalid after container modifications.

  • vector reallocation invalidates iterators and references.
  • erase usually invalidates the erased iterator.
  • list has more stable iterators because nodes are separately allocated.

Advice

Always check the container’s invalidation rules when modifying it during iteration.