#include "iostream.h"

// ================= CLASS A =======================

class A 
{
  friend class B;
  public:
    A() {                                // Constructor 1
	  cout<<"*CONS A #1\n";
	  a1=0; a2=0;                        
    };

    A(int i1,int i2=0) {                 // Constructor 2
	  cout<<"*CONS A #2\n";
	  a1=i1; a2=i2;                      
    };

    A(const A& pa) {                     // Constructor 3 (Copy Constructor)
	  cout<<"*CONS A #3\n";
	  a1=pa.a1; a2=pa.a2;                
    };

    void operator=(A& pa) {              // Member operator = 
	  pa.a1=a1; pa.a2=a2;
    }

    int sum() {return (a1+a2);};         // Member function sum

    ~A() {cout<<"*DEST A\n";};            // Destructor (do nothing)

  private:
    int a1,a2;           
};

// ================ CLASS B (composite class) ================

class B 
{
  friend ostream& operator<<(ostream &, B&);    // Friend (output) operator <<
  public:
    B (int i):b1(new A),b2(i),b3(i) {};         // Constructor  1

    B (B& bp):b2(bp.b2) {
      b1=new A(*(bp.b1));
      b2=bp.b2;
      b3=bp.b3;};                               // Constructor 2 (Copy)

    ~B () {delete b1;};                         // Destructor

    int& sum() {return b3;};                    // Member function

	virtual void print();                       // Member function

  protected:
    A *b1;     
    A b2;           
    int b3;	 
};

ostream& operator<<(ostream & os, B & bp)    // Friend (output) operator <<
{
  cout<<"*B<< "<<bp.b1->sum()<<" "<<bp.b2.sum()<<" "<<bp.sum()<<"\n";
  return os;
}

void B::print()                               // Member function
{
  cout<<"*Bprint "<<b1->a1<<" "<<b1->a2<<" ";
  cout<<b2.a1<<" "<<b2.a2<<" "<<b3<<" "<<"\n";
}

// ==================== CLASS C (derived class / subclass) ============

class C:public B 
{
  friend ostream& operator<<(ostream &, C&);  // Friend (output) operator <<
  public:
    C (int i):c1(0),B(i) {};                  // Constructor 
    void print();                             // Member function
    int& sum() {return c1;};                  // Member function
  private:
    int c1;          
};

void operator>(int i, C& cp)                  // Overloaded operator = 
{
  cp.sum()=i;
}

ostream& operator<<(ostream & os, C & cp)    // Friend (output) operator <<
{
  cout<<"*C<< "<<cp.b1->sum()<<" "<<cp.b2.sum()<<" ";
  cout<<cp.B::sum()<<" "<<cp.sum()<<"\n";
  return os;
}

void C::print()                               // Member function
{
  cout<<"*Cprint "<<b3<<" "<<c1<<"\n";
}

// ============== ordinary functions ====================

void f1(B & bp)
{
  cout<<"**** f1 ****\n";
  cout<<bp;
  bp.print();
}

void f2(B bp)
{
  cout<<"**** f2 ****\n";
  cout<<bp;
  bp.print();
}

void f3(B *bp)
{
  cout<<"**** f3 ****\n";
  cout<<*bp;
  bp->print();
}

template <class T>
void f4(T p)
{
  cout<<"**** f4 ****\n";
  cout<<p;
  p.print();
}

main()
{
  A a; 
  B b(5);
  C c(7);
  cout<<b<<c;

  A *pa = new A(3);
  cout<<"*"<<a.sum()<<" "<<pa->sum()<<"\n";
  *pa = a;
  cout<<"*"<<a.sum()<<" "<<pa->sum()<<"\n";

  int i=4;
  i > c;
  cout<<c;

  delete pa;

  b.print(); 
  c.print();

  f1(b);f1(c);
  f2(b);f2(c);
  f3(&b);f3(&c);
  f4<B>(b);f4<C>(c);
}
