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()