override: declare that you are overriding a method, like
@override in Java
virtual: enables inheritance polymorphism
draw operation to each would require making
the draw method be virtual (in C++)
Time *submissions[20] = { new Time(10, 22),
new Time(13, 45),
new MilTime(13, 18),
new MilTime(23, 0),
new MilTime(5, 10),
new Time(1, 15)
};
...
for(int i = 0; submissions[i] != nullptr; ++i) {
submissions[i]->print(cout);
cout << endl;
}
: public" in the class header: means derived from
: private", then base class public methods are
private in subclass
public class Firefly extends Insect (in Java)
class Insect {
public:
Insect(std::string species);
};
class Firefly : public Insect {
public:
Firefly(std::string species) : Insect(species) {
// note: no super in C++
// more code
}
};
class A {
public:
int f();
private:
int num;
};
class A;
new, it
stays on the heap until the program exits
delete to "undo" allocating the space:
Task *todo = new Task("eat", 0.75);
// code to process the item
delete todo;
new operation
todo:
delete todo;
cout << todo->description(); // dangling pointer reference!
// (random result)
todo to point to nothing:
delete todo;
todo = nullptr;
so later references to todo will cause run-time errors.
x is a nullptr,
delete x;
is perfectly fine - it does nothing
delete for every new
TaskList *items = new TaskList();
items->add(new Task("sleep", 8.0));
items->add(new Task("eat breakfast", 0.25));
items->add(new Task("learn something", 6.0));
...
delete items;
TaskList:
class TaskList {
...
public:
~TaskList() {
for(int i = 0; i < count; ++i)
delete todo[i];
}
TaskList also deletes any tasks
currently on the list
TaskList *items = new TaskList();
Task *x = new Task("do something", 1.0);
items->add(x);
delete items;
delete x; // breaks the heap!
class Student {
string name;
string address;
double total_grade_points, total_credits;
Schedule *schedule;
public:
Student() : schedule{new Schedule},
total_grade_points{0.0},
total_credits{0.0},
name(""),
address("")
{ }
virtual ~Student() { delete schedule; }
double gpa() const { return total_grade_points / total_credits; }
virtual void add(Course *new_course);
};
class SpecialStudent : public Student {
Certificate *cert;
public:
SpecialStudent() : cert{new Certificate} { }
~SpecialStudent() { delete cert; }
void add(Course *new_course); ...
};
Student object, also allocate
a Schedule object
delete student; - works, but doesn't reclaim the space for
the schedule
~Student() { delete schedule; }
add is virtual; can be redefined in subclasses
add is virtual, then ~Student must be:
virtual ~Student() { delete schedule; }
void doSomethingNice() {
Student *someStudent = new SpecialStudent();
...
delete someStudent;
}
class AbstractContainer {
public:
virtual double& operator[](int index) = 0;
virtual int size() = 0;
virtual ~AbstractContainer() { }
};
= 0 syntax means the method is abstract or pure
virtual
AbstractContainer
simple_vector.h
double& in operator[] means we can
assign as well as use the value
void swapFirstLast(AbstractContainer &nums) {
int last_index = nums.size() - 1;
assert(last_index > 0); // last_index can be 0!
double save = nums[0];
nums[0] = nums[last_index];
nums[last_index] = save;
}
and the call
FixedVector xs;
...
// first shall be last, last shall be first:
swapFirstLast(xs);
How does this work?
nums passed by reference
to swapFirstLast
_size):
AbstractContainer &nums = xs;
int last_index = nums._size - 1;
assert(last_index > 0);
double *p_start = &nums.elements[0];
double *p_end = &nums.elements[last_index];
double save = *p_start;
*p_start = *p_end;
*p_end = save;
so return by reference is the same as having a "hidden" pointer
x.do_something()
Question: how do we find the code to execute for do_something?
long fact(int n) { return n <= 0 ? 1 : n * fact(n - 1); }
long pow2(int n) { return n <= 0 ? 1 : 2 * pow2(n - 1); }
long (number_cruncher*)(int);
if ( x > 0 )
number_cruncher = pow2;
else
number_cruncher = fact;
cout << (*number_cruncher)(x); // either pow2(x) or fact(x)
class X {
public:
void a();
virtual void b();
virtual void c();
};
class Y : public X {
public:
void b() override;
void d();
virtual void e();
};
X *something = new X(), *other = new Y();
+-------------------+
| &X::b | &X::c |
+-------------------+
+-----------------------------+
| &Y::b | &X::c | &Y::e |
+-----------------------------+
Y *confused = new X();
isn't just a "little" illegal!!
train.s, starting at
line 771
multiple_inheritance.cpp
in sample code directory
FixedVector; consider when destructor called:
{
int n;
cin >> n;
FixedVector xs(n);
...
xs[1] = 23.4; // this works because [] returns double&
xs[0] = xs[1] / 2.0;
...
{
FixedVector ys(n * 2);
...
} // ys destroyed here
...
} // xs destroyed here
new, delete follows
FixedVector(std::initializer_list<double>)
initializer_list is an STL type known to the compiler
FixedVector v1 = {1, 2, 3, 4, 5};
megavector.h
void initializeUpSequence(AbstractContainer *container,
double start, double step) {
double value = start;
for(int ix = 0; ix < container->size(); ++ix) {
(*container)[ix] = value;
value += step;
}
}
...
AbstractContainer *some_numbers = new MegaVector();
initializeUpSequence(some_numbers, 1.0, 0.1);
double sum = 0.0;
for(int i = 0; i < some_numbers->size(); ++i)
sum += (*some_numbers)[i];
void initializeUpSequence(AbstractContainer container,
double start, double step);
(with no *)
void initializeUpSequence(AbstractContainer &container,
double start, double step);
initializeUpSequence(nullptr, 1, 2);
nullptr to a reference
double first_and_last(MegaVector nums) {
double sum = nums[0] + nums[nums.size() - 1];
return sum;
}
MegaVector xs;
...
double s = first_and_last(xs);
double first_and_last(MegaVector &nums) {
double sum = nums[0] + nums[nums.size() - 1];
return sum;
}
double sum = (nums[0] = 0.0) + nums[nums.size() - 1];
double first_and_last(const MegaVector &nums) {
double sum = nums[0] + nums[nums.size() - 1];
return sum;
}
const methods:
revised_vector.h
const&
< on students:
bool operator<(const Student &a, const Student &b) {
return a.name() < b.name();
}
std::string by const
reference:
char firstInitial(const std::string &name) {
return toupper(name[0]);
}
Point with
different purposes
namespace myNames
{
void getData(int&);
};
namespace yourNames
{
void getData(int&);
};
iostream
namespace std {
class istream { ... };
istream cin;
class ostream { ... };
ostream cout;
};
and file string:
namespace std {
class string { ... };
};
hence the need for using namespace std
or std::cin, std::string
namespace myNames
{
void getData(int &x) { ... }
};
void yourNames::getData(int &y) {...}
int i; myNames::getData(i); yourNames::getData(i);
using myNames::getData;
All calls to getData now refer to myNames implicitly
using namespace myNames;
for easy access to everything
string, cin, cout are all names
in namespace std
a::f or b::f and
correctness depends on which one you use, reconsider your naming scheme!
xyz.h and xyz.cpp
xyz.h can be included multiple times
#ifndef/#define/#endif
#ifdef magic"
xyz.h must be complete: if it refers to something
in another header, include that header
X* data,
can simply write
class X;
to declare that X is a class.
std::string whenever refer to standard classes
xyz.cpp: #include "xyz.h"
xyz.h is complete
const&
parameters
#includes
class X {
public:
X();
void update();
void print() const;
};
void f(X a) { a.update(); }
void g(X *b) { b->update(); }
void h(X* c) { c->update(); }
void i(X &d) { d.update(); }
void j(const X &e) { e.update(); }
void k(const X &u) { u.print(); }
void l(X &v) { v.print(); }