#include <string>
#include <iostream>

using std::string;
using std::cout;

// Example: Polymorphic partial assign; popular in high-level languages, such as C#

class class1 {
private: int _a, _b;
public:
	class1(int a, int b) : _a(a), _b(b) { }
	virtual void assign(const class1 *pother) {
		if(!pother) return;
		_a = pother->_a;
		_b = pother->_b;
	}
	virtual void print() const { cout << "a = " << _a  << ", b = " << _b; }
};

class class2 : public class1 {
private: int _c, _d;
public:
	class2(int a, int b, int c, int d) : class1(a, b), _c(c), _d(d) { }
	void assign(const class1 *pother) {
		class1::assign(pother);
		if(const class2 *pother_class2 = dynamic_cast<const class2*>(pother)) {
			_c = pother_class2->_c;
			_d = pother_class2->_d;
		}
	}
	void print() const { class1::print(); cout << ", c = " << _c << ", d = " << _d; }
};

class class3 : public class2 {
private: int _e;
public:
	class3(int a, int b, int c, int d, int e) : class2(a, b, c, d), _e(e) { }
	void assign(const class1 *pother) {
		class2::assign(pother); // copy base state
		if(const class3 *pother_class3 = dynamic_cast<const class3*>(pother)) {
			_e = pother_class3->_e;
		}
	}
	void print() const { class2::print(); cout << ", e = " << _e; }
};

int main() {
	// -- creating test objects -------------------------------------------

	class1 *pobj1 = new class1(1, 2);
	cout << "*pobj1 = { ";
	pobj1->print();
	cout << " }\n";

	class1 *pobj2 = new class2(3, 4, 5, 6);
	cout << "*pobj2 = { ";
	pobj2->print();
	cout << " }\n";

	class1 *pobj3 = new class3(7, 8, 9, 10, 11);
	cout << "*pobj3 = { ";
	pobj3->print();
	cout << " }\n";

	// -- testing polymorphic partial assignment -------------------------

	// copy advanced object's state to basic object
	pobj1->assign(pobj2);
	cout << "pobj1->assign(pobj2);\n";
	cout << "updated *pobj1 = { ";
	pobj1->print();
	cout << " }\n";

	// copy basic object's state into advanced object (Why can we not rely on slicing?)
	pobj3->assign(pobj1);
	cout << "pobj3->assign(pobj1);\n";
	cout << "updated *pobj3 = { ";
	pobj3->print();
	cout << " }\n";

	// -- cleaning up ----------------------------------------------------
	
	delete pobj1;
	delete pobj2;
	delete pobj3;

	return 0;
}
