class SList { class SNode { // can nest classes; note this is private public: string item; SNode *next; SNode(string it, SNode *n = nullptr) // default value : item{it}, next{n} { } } *front, *tail; // note * applies only to next item public: SList() { front = tail = nullptr; } ~SList() { clear(); } bool empty() const { return front == nullptr; } void push_front(string x) { if ( front ) front = new SNode(x, front); else { front = tail = new SNode(x); } } void push_back(string x) { if ( empty() ) push_front(x); else { tail->next = new SNode(x); tail = tail->next; } } string take_front() { if ( empty() ) throw "error: no first element of empty list"; string result = front->item; SNode *tmp = front->next; delete front; front = tmp; if ( empty() ) tail = nullptr; return result; } void clear() { while ( !empty() ) take_front(); } };What happens when we execute
SList a; a.push_back("laura"); a.push_back("paul"); if ( !a.empty() ) { SList b; b = a; a.push_back("zebra"); ... code using b ... }
void SList::setTo(const SList &other); // create a copy of other
void print(SList list_copy) // note get a copy! { while ( !it.empty() ) cout << it.take_front() << endl; } ... print(a);
SList double(const SList& input) { SList result = input; result += input; return result; }
public: ... SList(const SList &other);
SList& operator=(const SList &other) { if ( this != &other ) { clear(); setTo(other); } return *this; }
SList a, b, c; ... a = b = c; (a = b).push_back("why?");
class Base { ... public: ~Base(); }; class Derived : public Base { ... public: ~Derived(); }; Derived *one = new Derived; Base *two = new Derived;Derived::~Derived is executed for
delete one;but not for
delete two;Potential memory leak!
class Event { Ticket *tickets[NUM_SEATS]; int sold; public: ... ~Event() { for(int i = 0; i < sold; ++i) delete tickets[i]; } ... }; class Concert : public Event { Reservation *venue; public: ~Concert() { delete venue; } };with
Event *nextWeek = new Concert; ... delete nextWeek; // executes Event::~Event, not Concert::~Concert
class Base { ... public: virtual ~Base(); };and
virtual ~Event() { for(int i = 0; i < sold; ++i) delete tickets[i]; }Generally, compilers will issue warnings, but you have to know why it's necessary.
SList a, b, c; ... c = a + b;
SList(SList&& src); // move constructor SList& operator=(SList&& src); // move assignment
SList::Slist(SList&& src) : front{src.front}, tail{src.tail} { src.front = src.tail = nullptr; }
SList& operator=(SList &&src) { if ( this != &src ) { clear(); front = src.front; tail = src.tail; src.front = src.tail = nullptr; } return *this; }
class X { ... other stuff ... public: X(const X&) =delete; const X& operator=(const X&) =delete; X(X&&) =delete; void operator=(X&&) =delete; };
#define NOCOPY(T) T(const T&)=delete;T(const T&&)=delete;void operator=(const T&)=delete;void operator=(const T&&)=delete;which can be used as
class Roster { ... NOCOPY(Roster); };
rwh::string getName(); rwh::string name = getName(), ghost = "Casper"; if ( name == "Waldo" ) cout << "Found him!" << endl; else if ( name == ghost ) cout << "I'm outta here!" << endl;
rwh::string
class rwh::string {
public:
bool operator==(string other) const { // just one argument!
if ( this == &other )
return true;
// do charwise comparison (see rwh_string.cpp
)
}
bool operator==(const char* other) const {
return *this == string(other); // call above method
}
// ... rest of class
};
class A { public: void f(int n); void g(float u) const; ... } xyz;
_A_f(A* hidden_this, int n) { ... } _A_g(const A* hidden_this, float u) { ... }so the calls
xyz.f(3)
and xyz.g(4.5);
turn into
_A_f(&xyz, 3); _A_g(&xyz, 4.5);
const
methods just have const
first parameters!
bool operator==(const char*, const char*); // NOT LEGAL
==
when the first argument is
a rwh::string
and the second is either another string
or char*
bool operator==(const char* other, const rwh::string str) { return str.operator==(other); // or str == other }This method would be outside the class.
ghost.c_str()