#include <iostream>

// Представьте, что доступа к этопу коду мы не имеем. Физически, организационно
// или просто люди, которые его разрабатывали или поддерживали, давно отсуствуют.
// На самом деле, это не сильно отличается от первого предположения
class LegacyObj
{
public:
	LegacyObj(int i):_i(new int(i)){}

	~LegacyObj(){
        clear();
    }

    LegacyObj(const LegacyObj& rhs){
        clear();
        _i = new int(*rhs._i);
    }

    LegacyObj& operator=(const LegacyObj& rhs){
        
        // этот вызов явно лишний, но ничего с ним сделать не можем
        clear();

        // копируем наши "ресурсы"
        _i = new int(*rhs._i);
        
        // настоящие ковбои никогда не проверяют &rhs != this
        return *this;
    }

    void clear(){
        delete _i;
        // _i = 0; - если бы еще обнулили этот указатель, это нам бы помогло при отладке
    }

    void print_me(){
        std::cout << *_i << std::endl;
    }

private:
    // вместо int* могут быть любые ресурсы, требующие аллокации
    int* _i;
};

// Какая-то обертка вокруг старого кода

// Версия с проверкой
class WrapperWithCheck
{
public:
    
    WrapperWithCheck(int a, int b, int c):_a(a),_b(b),_c(c){}

    WrapperWithCheck(const WrapperWithCheck& rhs):_a(rhs._a),_b(rhs._b),_c(rhs._c){}

    WrapperWithCheck& operator=(const WrapperWithCheck& rhs){
        if(&rhs == this)
            return *this;

        _a = rhs._a;
        _b = rhs._b;
        _c = rhs._c;
        return *this;
    }

    void print_me(){
        _a.print_me();
        _b.print_me();
        _c.print_me();
    }

private:
    LegacyObj _a;
    LegacyObj _b;
    LegacyObj _c;
};


// Версия без проверки
class WrapperWithoutCheck
{
public:

    WrapperWithoutCheck(int a, int b, int c):_a(a),_b(b),_c(c){}

    WrapperWithoutCheck(const WrapperWithoutCheck& rhs):_a(rhs._a),_b(rhs._b),_c(rhs._c){}

    WrapperWithoutCheck& operator=(const WrapperWithoutCheck& rhs){
        _a = rhs._a;
        _b = rhs._b;
        _c = rhs._c;
        return *this;
    }

    void print_me(){
        _a.print_me();
        _b.print_me();
        _c.print_me();
    }

private:
    LegacyObj _a;
    LegacyObj _b;
    LegacyObj _c;
};

int main() {
    
    {
        WrapperWithCheck wrapper_with_check(1, 2, 3);
        WrapperWithCheck other_wrapper(5, 6, 7);
        std::cout << "Object with check" << std::endl;
        wrapper_with_check.print_me();
        WrapperWithCheck& wrapper_ref = wrapper_with_check;

        // присваивание другому объекту
        other_wrapper = wrapper_with_check;

        // присваивание себе
        wrapper_with_check = wrapper_ref;
        std::cout << "After assignment" << std::endl;
        wrapper_with_check.print_me();
    }

    {
        WrapperWithoutCheck wrapper_without_check(1, 2, 3);
        WrapperWithoutCheck other_wrapper(5, 6, 7);
        std::cout << "Object without check" << std::endl;
        wrapper_without_check.print_me();
        WrapperWithoutCheck& wrapper_ref = wrapper_without_check;

        // присваивание другому объекту
        other_wrapper = wrapper_without_check;

        // присваиваем себе
        wrapper_without_check = wrapper_ref; 
        std::cout << "After assignment" << std::endl;
        // вывод здесь не определен
        // если бы обнулили указатель в clear() - получили бы debug assert и release crash
        wrapper_without_check.print_me();
    }
    return 0;
}
