Outline
Other Material
Object-Oriented programming holds encapsulation as an ideal, bundling all actions in one place. Can make for ``fat'' classes.
The STL separates data structures and algorithms. The data structures are relatively small. The algorithms are many. Can be mixed and matched.
Allows for a much smaller class library.
By convention, containers return a starting value in response to begin(), and a past-the-end value in response to end().
For example, to shuffle a vector of values:
random_shuffle (aVector.begin(), aVector.end(), randomInteger);
To see what iterators must do, consider a typical algorithm:
template <class T>
iterator find
(iterator first, iterator last, T & value)
{
while (first != last && *first != value) ++first;
return first;
}
Could be used to find values in an array, or in a list:
int data[100]; ... int * where = find(data, data+100, 7); list<int>::iterator where = find(aList.begin(), aList.end(), 7);
What makes iterators possible is that all of these can be overloaded.
In C++ even the function call operator (parenthesis operator) can be overloaded. Allows for creation of objects that can be used like functions.
class biggerThanThree {
public:
bool operator () (int v)
{ return v > 3; }
};
Can be used to find the first value bigger than 3, for example.
Objects can take arguments computed at run-time, specialize functions in a way that simple functions cannot:
class biggerThan {
public:
biggerThan (int x) : testValue(x) { }
const int testValue;
bool operator () (int val)
{ return val > testValue; }
};
list<int>::iterator firstBig =
find_if (aList.begin(), aList.end(), biggerThan(12));
In functional languages, this kind of object is sometimes known as a curry.
A business, WorldWideWidgetWorks, manufactures widgets.
class Widget {
public:
// constructors
Widget () : id_number (0) { }
Widget (int a) : id(a) { }
// operations
int id () { return id_number; }
void operator = (Widget & rhs)
{ id_number = rhs.id_number; }
bool operator == (Widget & rhs)
{ id_number == rhs.id_number; }
bool operator < (Widget & rhs)
{ id_number < rhs.id_number; }
protected:
int id_number; // widget identification number
};
Inventory is two lists - items on hand, items on back order.
class inventory {
public:
// process order for widget type wid
void order (int wid);
// receive widget of type wid
// in shipment
void receive (int wid);
protected:
list <Widget> on_hand;
list <int> on_order;
};
void inventory::receive (int wid)
// process a widget received in shipment
{ cout << "Received shipment of widget type " << wid << endl;
list<int>::iterator we_need =
find (on_order.begin(), on_order.end(), wid);
if (we_need != on_order.end()) {
cout << "Ship " << Widget(wid) << " to fill back order" << endl;
on_order.erase(we_need);
}
else
on_hand.push_front(Widget(wid));
}
void inventory::order (int wid)
// process an order for a widget with given id number
{ cout << "Received order for widget type " << wid << endl;
list<Widget>::iterator we_have =
find_if(on_hand.begin(), on_hand.end(), WidgetTester(wid));
if (we_have != on_hand.end()) {
cout << "Ship " << *wehave << endl;
on_hand.erase(we_have);
}
else {
cout << "Back order widget of type " << wid << endl;
on_order.push_front(wid);
}
}
Created like an instance of a class, works like a function.
class WidgetTester {
public:
WidgetTester (int id) : test_id(id) { }
int test_id;
// define the function call operator
bool operator () (Widget & wid)
{ return wid.id() == test_id; }
};
typedef map<string, int> stringVector;
typedef map<string, stringVector> graph;
string pendleton("Pendleton");
string pensacola("Pensacola");
string peoria("Peoria");
string phoenix("Phoenix");
string pierre("Pierre");
string pittsburgh("Pittsburgh");
string princeton("Princeton");
string pueblo("Pueblo");
graph cityMap;
cityMap[pendleton][phoenix] = 4;
cityMap[pendleton][pueblo] = 8;
cityMap[pensacola][phoenix] = 5;
cityMap[peoria][pittsburgh] = 5;
cityMap[peoria][pueblo] = 3;
cityMap[phoenix][peoria] = 4;
cityMap[phoenix][pittsburgh] = 10;
cityMap[phoenix][pueblo] = 3;
cityMap[pierre][pendleton] = 2;
cityMap[pittsburgh][pensacola] = 4;
cityMap[princeton][pittsburgh] = 2;
cityMap[pueblo][pierre] = 3;
struct DistancePair {
unsigned int first;
string second;
DistancePair() : first(0) { }
DistancePair(unsigned int f, string & s)
: first(f), second(s) { }
};
bool operator < (DistancePair & lhs, DistancePair & rhs)
{ return lhs.first < rhs.first; }
void shortestDistance(const graph & cityMap,
const string & start, stringVector & distances)
{
// process a priority queue of distances to cities
priority_queue<DistancePair, vector<DistancePair> ,
greater<DistancePair> > que;
que.push(DistancePair(0, start));
while (! que.empty()) {
// pull nearest city from queue
int distance = que.top().first;
string city = que.top().second;
que.pop();
// if we haven't seen it already, process it
if (0 == distances.count(city)) {
// then add it to shortest distance map
distances[city] = distance;
// and put values into queue
stringVector::iterator start, stop;
start = cityMap[city].begin();
stop = cityMap[city].end();
for (; start != stop; ++start)
que.push(DistancePair(distance + (*start).second,
(*start).first));
}
}
}
Given that the STL is not OO, does this mean that OOP is dead?
No, only that inspiration can come from a number of sources, and not all problems are best solved in an OOP fashion.
However, many problems ARE best approached in an OOP manner.
Bottom line - know as much as you can about as many styles of programming as you can, and use the style most appropriate to the problem.