94. Initialize Filter3 Class

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

  • Private Members
    • const int id
    • int coeff[3]
    • int lastOutput
  • Constructor Requirements
    • Create the following constructor:
      • Filter3(int filterId, int c0, int c1, int c2)
    • The constructor must initialize all members using an initialization list:
      • id initialized with filterId
      • coeff initialized as {c0, c1, c2}
      • lastOutput initialized to 0
    • ❗ Assignment inside the constructor body is not allowed.
  • Public Methods
    • void apply(int x)
      • Updates the output using the formula:
        • lastOutput = x * coeff[0] + coeff[1] + coeff[2]; 
    • int read()
      • Returns the current value of lastOutput

In main()

  1. Read id, c0, c1, c2
  2. Construct a Filter3 object
  3. Read two input samples s1 and s2
  4. Call apply(s1) and then apply(s2)
  5. Print the final output using read()

 

Example Input

7 2 3 1
10 4

Example Output

12

Explanation

  • Coefficients = {2, 3, 1}
  • apply(10)10 * 2 + 3 + 1 = 24
  • apply(4)4 * 2 + 3 + 1 = 12
  • Final output = 12

 

Constraints

  • Overflow handling is not required
  • Input values remain within safe integer limits
  • Output must match exactly

 

 

 

Need Help? Refer to the Quick Guide below

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.

Syntax & Usage

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).
  • Copy Constructor: Defines how to clone an object (crucial for Deep Copies of pointers).
  • = 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; 
};

Initialization vs Assignment Flow

FeatureAssignment (Inside Body)Initializer List (: x(10))
MechanismVariable created (default), then overwritten.Variable created with value.
PerformanceSlower (Two steps).Faster (One step).
ConstraintsCannot init const or Reference.Required for const/Reference.

Relevance in Embedded/Firmware

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.

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Heavy WorkAvoid 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.

class A { int y; int x; A(): x(1), y(x) {} } reads uninitialized memory because y is declared first.

❌ Implicit ConversionWithout explicit, void func(Driver d) can be called as func(100), silently creating a 100-byte driver. This is confusing and dangerous.
✅ Default ConstructorIf you define any constructor, the compiler removes the default (empty) one. You must explicitly add GPIO() = default; if you still need it.