#include <iostream>
#include "Str.h" // from lec11_code/str

using namespace std;

Str foo1() {
  cout << "Entered foo1()" << endl;
  Str s("foo1"); // implicit constructor

  cout << "End of foo1()" << endl;
  return s; // implicit copy constructor (unless optimized out)
} // implicit destructor

Str* foo2() {
  cout << "Entered foo2()" << endl;
  // Str s("foo2"); // BAD!
  Str *s_ptr = new Str("foo2");

  cout << "End of foo2()" << endl;
  return s_ptr; // caller must delete!
  // return &s; // BAD!
}

Str& foo3() {
  cout << "Entered foo3()" << endl;
  // Str s("foo3"); // BAD!
  Str *s_ptr = new Str("foo2");

  cout << "End of foo3()" << endl;
  return *s_ptr; // caller must delete by reference -- very sketchy
  // return &s; // BAD!
}

void usingFoo() {
  cout << "Before foo1()" << endl;
  Str val = foo1(); // return value copy assigned into val;
  cout << "After foo1()" << endl;

  cout << endl;

  cout << "Before foo2()" << endl;
  Str* val_ptr = foo2();
  delete val_ptr;
  cout << "After foo2()" << endl;

  cout << endl;

  cout << "Before foo3()" << endl;
  // val = foo3(); // BAD! We would lose the pointer!
  Str& val_ref = foo3();
  delete &val_ref; // correct, but sketchy
  cout << "After foo3()" << endl;
}

int main(int argc, char** argv) {
  usingFoo();
  return 0;
}