190. Self-Assignment Check

In C++, the assignment operator (=) is just a function. If you write obj = obj; (assigning an object to itself), a naive implementation might destroy the object's data before trying to copy it, leading to corruption or crashes. This is especially dangerous with resources like buffers or file handles.

To prevent this, every assignment operator must first check: "Is the source address the same as my address?"

Your task is to implement a class Buffer.

  1. Private member: int id.
  2. Constructor: Buffer(int i) : id(i) {}.
  3. Operator= (Assignment):
    • Check if (this == &other).
    • If true, print Self-Assignment Detected and return *this immediately without changing anything.
    • If false, copy id from other, print Assignment Complete, and return *this.
  4. Method void log(): Print Buffer ID: <id>.

Program Flow:

  1. Read integer N.
  2. Loop N times.
  3. Read string cmd.
  4. If cmd is "ASSIGN":
    • Read two integers id1 and id2.
    • Create two Buffer objects (b1, b2) with these IDs.
    • Perform b1 = b2;.
    • Call b1.log().
  5. If cmd is "SELF":
    • Read integer id.
    • Create Buffer b1(id).
    • Perform b1 = b1; (Self-Assignment).
    • Call b1.log().

Input Format:

  • First line: Integer N.
  • Next N lines: String cmd, followed by values.
  • Input is provided via standard input (stdin).

Output Format:

  • "ASSIGN": Assignment Complete followed by Buffer ID: <new_id>
  • "SELF": Self-Assignment Detected followed by Buffer ID: <original_id>
  • Each output on a new line.

Example: 

Example 1

Input:

2
ASSIGN 10 20
SELF 99

Output:

Assignment Complete
Buffer ID: 20
Self-Assignment Detected
Buffer ID: 99

Constraints:

  • Must use if (this == &other) inside operator=.
  • Must return *this to allow chaining.

 

 

 

 

Need Help? Refer to the Quick Guide below

Inside every non-static member function, C++ provides a hidden pointer named this.

It points to the address of the object that called the function.

Think of it as the word "Me" or "My" in English.

  • If Alice says "My pocket", she refers to Alice's pocket.

  • If Bob says "My pocket", he refers to Bob's pocket.

  • The code (the sentence) is the same, but the context (this) changes depending on who speaks.

Syntax & Usage

A. Resolving Name Conflicts

The most common use is when a function argument has the same name as a class member variable. this-> explicitly tells the compiler "use the member variable, not the local argument."

class Servo {
    int pos; // Member variable
public:
    // Argument 'pos' shadows member 'pos'
    void setPos(int pos) {
        // pos = pos;      // WRONG: Assigns argument to itself.
        this->pos = pos;   // RIGHT: "My pos" = argument "pos".
    }
};

B. Method Chaining (Fluent Interfaces)

You can return *this (the object itself) to chain function calls on a single line. This is common in configuration patterns (Builders).

class LCD {
public:
    LCD& clear() { 
        /* clear screen */ 
        return *this; // Return reference to myself
    }
    LCD& setCursor(int x, int y) { 
        /* move cursor */ 
        return *this; 
    }
    LCD& print(const char* s) { 
        /* print text */ 
        return *this; 
    }
};

int main() {
    LCD screen;
    // Chaining calls:
    screen.clear().setCursor(0, 1).print("Ready");
}

Under the Hood (The Hidden Argument)

How does one function code work for 100 different objects?

The compiler secretly modifies your function. It adds a hidden first argument: the pointer to the object.

Your C++ Code

What the Compiler Actually Generates (C-Style)

void Servo::move(int p) { ... }

void Servo_move(Servo* const this, int p) { ... }

myServo.move(90);

Servo_move(&myServo, 90);

  • Note: This is why Static Functions do not have this. They are not passed this hidden argument, so they don't know which object to touch.

Relevance in Embedded/Firmware

A. RTOS Tasks & C-Style Callbacks

Most RTOSs (FreeRTOS, ThreadX) and Hardware Drivers use C APIs. They allow you to pass a void* context parameter to callbacks.

We pass this so the static C callback knows which C++ object to restore.

class Button {
public:
    void onClick() { printf("Clicked!\n"); }

    // Static wrapper compatible with C-style callback
    static void callback_wrapper(void* context) {
        // 1. Cast 'void*' back to 'Button*'
        Button* self = static_cast<Button*>(context);
        
        // 2. Call the actual member function using the restored pointer
        self->onClick();
    }
};

// Registering interrupt: pass 'this' as the context
attachInterrupt(PIN_5, Button::callback_wrapper, this);

B. Self-Assignment Check

In assignment operators (operator=), you must check if the user is trying to assign an object to itself (obj = obj;). If you don't check this, you might delete your own data before copying it (Disaster).

Buffer& operator=(const Buffer& other) {
    if (this == &other) { // Am I copying myself?
        return *this;     // Do nothing.
    }
    // Proceed with copy...
}

Common Pitfalls (The Danger Zone)

Pitfall

Details

delete this

Is it legal? Yes. Is it dangerous? Extremely. If you write delete this;, the object destroys itself. Accessing any member variable afterwards causes a crash. Used rarely (e.g., in "Fire and Forget" tasks).

❌ Using in Static

Static functions belong to the class, not an instance. Trying to use this inside a static method causes a compile error: "invalid use of member in static member function".

❌ Capture in Lambdas

In C++11 Lambdas, using [=] captures variables by value, but it captures this as a pointer. If the object dies but the lambda runs later (async callback), accessing members via the captured this will crash (Use After Free).

Summary Checklist

  • Use this->variable to fix shadowing names.

  • Return *this to enable method chaining (obj.func1().func2()).

  • Pass this as void* context when integrating with C libraries or RTOS tasks.

  • Never access this inside a static function.