Create a class Filter3 that represents a simple 3-coefficient digital filter used in embedded signal processing.
This problem focuses on correct use of constructor initialization lists in Embedded C++, which are mandatory when initializing const members and fixed-size arrays.
All internal members must be initialized using a constructor initialization list, not by assignment inside the constructor body.
Class Requirements
const int idint coeff[3]int lastOutputFilter3(int filterId, int c0, int c1, int c2)id initialized with filterIdcoeff initialized as {c0, c1, c2}lastOutput initialized to 0void apply(int x)lastOutput = x * coeff[0] + coeff[1] + coeff[2]; int read()lastOutputIn main()
id, c0, c1, c2Filter3 objects1 and s2apply(s1) and then apply(s2)read()
Example Input
7 2 3 1
10 4
Example Output
12
Explanation
{2, 3, 1}apply(10) → 10 * 2 + 3 + 1 = 24apply(4) → 4 * 2 + 3 + 1 = 1212
Constraints
A Constructor is a special member function that runs automatically when an object is created. Its purpose is to guarantee that the object starts in a valid state (e.g., pointers assigned, hardware initialized, invariants checked) before any other code uses it.
It has no return type and shares the same name as the class.
1. Basic & Parameterized Constructors
The most common forms used to initialize variables.
class GPIO {
int pin;
public:
// 1. Default Constructor (No args)
GPIO() { pin = 0; }
// 2. Parameterized Constructor (With args)
GPIO(int p) { pin = p; }
};
GPIO led; // Calls GPIO()
GPIO motor(9); // Calls GPIO(int)2. Member Initializer Lists (Crucial for Efficiency)
Initializes variables before the constructor body runs. This is mandatory for const members and References, and avoids "double-writing" variables.
class Sensor {
const int id;
int& bus_ref;
public:
// Syntax: : member(value), member(value)
Sensor(int i, int& bus) : id(i), bus_ref(bus) {
// Body is empty; work is already done.
}
};3. Advanced Variants (explicit, Copy, delete)
These control how objects are created or copied, preventing dangerous bugs.
explicit: Prevents accidental implicit conversions (e.g., passing int where a Driver object is expected).= delete: Disables a constructor (used for Singletons or unique hardware drivers).class Driver {
int* buffer;
public:
// PREVENT implicit conversion from int to Driver
explicit Driver(int size) { buffer = new int[size]; }
// CUSTOM Copy Constructor (Deep Copy)
Driver(const Driver& other) {
buffer = new int[10];
memcpy(buffer, other.buffer, 10 * sizeof(int));
}
// DISABLE Copying entirely (Common for hardware drivers)
// Driver(const Driver&) = delete;
};| Feature | Assignment (Inside Body) | Initializer List (: x(10)) |
|---|---|---|
| Mechanism | Variable created (default), then overwritten. | Variable created with value. |
| Performance | Slower (Two steps). | Faster (One step). |
| Constraints | Cannot init const or Reference. | Required for const/Reference. |
1. Impossible-to-Forget Initialization
In C, forgetting UART_Init() crashes the system.
In C++, constructors force you to provide configuration (e.g., Baud Rate) at creation time. The object never exists in an uninitialized state.
2. RAII (Resource Acquisition Is Initialization)
The "Scope Lock" pattern: A constructor acquires a resource (disables interrupts, takes a mutex), and the Destructor releases it.
{
InterruptLock lock; // Constructor: __disable_irq();
// Critical Section
} // Destructor: __enable_irq(); automatic at end of scope.
3. Unique Hardware Access
Using = delete on copy constructors ensures you don't have two software objects trying to control the same physical UART peripheral, preventing race conditions.
| Pitfall | Details |
|---|---|
| ❌ Heavy Work | Avoid complex hardware delays or checks in constructors for Global/Static objects. They run before main(), which can make the system appear to hang at boot. |
| ❌ Initializer Order | Members are initialized in the order they are declared in the class, NOT the order in the list.
|
| ❌ Implicit Conversion | Without explicit, void func(Driver d) can be called as func(100), silently creating a 100-byte driver. This is confusing and dangerous. |
| ✅ Default Constructor | If you define any constructor, the compiler removes the default (empty) one. You must explicitly add GPIO() = default; if you still need it. |