38
Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee •GDC Roadtrip Dec 1999

Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Embed Size (px)

Citation preview

Page 1: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Embracing the C++ STL:Why Angle Brackets are

Good for You

Pete Isensee

•GDC Roadtrip Dec 1999

Page 2: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Introduction

• STL Background & History• Key Concepts• Containers, Iterators and Algorithms• Efficiency and Thread Safety• Tips and Tricks

•GDC Roadtrip Dec 1999

Page 3: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Disadvantages

• Template syntax• Difficult to read & decipher• Poor or incomplete compiler support• Code bloat potential• No constraints on template types• Limited container types

•GDC Roadtrip Dec 1999

Page 4: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Key Concepts

• Generic algorithms• Container classes (data structures)• Iterators : “container walkers” for accessing

container elements (smart pointers)• Iterators provide an abstraction of container

access, which in turn allows for generic algorithms

• Iterator invalidation

•GDC Roadtrip Dec 1999

Page 5: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Key Concepts (cont.)

• Ranges: C/C++ “past-the-end” pointerT Wallace[N];T* p = (Wallace + N); // valid pointerT w = *(Wallace + N); // invalid dereference

c.begin() == (Wallace); // first elementc.end() == (Wallace + N); // valid iterator*c.end(); // invalid dereference

• end() - begin() = size()• if (begin() == end()) container is empty

• for (iter i = begin(); i != end(); ++i)•GDC Roadtrip Dec 1999

Page 6: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Key Concepts (cont.)

• Linear search exampletemplate <class InputItr, class T> InputItrfind(InputItr bg, InputItr end, const T& val){ while (bg != end && *bg != val) ++bg; return (bg); }

const int nSize = 4;int Gromit[nSize] = { 5, 18, 23, 9 };int* pFind = find(Gromit, Gromit + nSize, 23);

vector<int> Preston;vector<int>::iterator i = find(Preston.begin(), Preston.end(), 4);

•GDC Roadtrip Dec 1999

Page 7: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Putting the STL into Action

• Include files have no “.h”• Standard namespace

#include <cstdio> // new include method#include <vector> // vector container#include <algorithm> // STL algorithmsusing namespace std; // assume std::

vector<int> Chuck; // declare a growable arrayChuck.push_back(1); // add an elementfind(Chuck.begin(), Chuck.end(), 1);

•GDC Roadtrip Dec 1999

Page 8: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Containers

• Containers contain elements; they “own” the objects

• Containers provide iterators that point to its elements.

• Containers provide a minimal set of operations for manipulating elements

•GDC Roadtrip Dec 1999

Page 9: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Containers (cont.)

Container Description Keys

vector dynamic array deque dynamic array -- both ends list linked list set sorted list of keys no duplicate keys map sorted list of key and value pairs no duplicate keys multiset sorted list of keys duplicate keys OK multimap sorted list of key and value pairs duplicate keys OK

•GDC Roadtrip Dec 1999

Minimum container object requirements

X() // default constructorX(const X&) // copy constructorX& operator = (const X&) // assignment opbool operator < (const X&) // comparison opbool operator == (const X&) // comparison op

Page 10: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Vector

• Dynamic array• Fast ins/erase from end of vector• reserve(), capacity()• Contiguous block of memory• Obliged to grow by some factor (2x) when

size() exceeds capacity()

•GDC Roadtrip Dec 1999

Page 11: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Deque

• Double-ended queue (“deck”)• Fast ins/erase at begin and end• Directory array of pointers to nodes, where

each node is small array of T

•GDC Roadtrip Dec 1999

Page 12: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

List

• Doubly-linked list• Fast insert/erase; no random access• Special functions: splice(), merge()

•GDC Roadtrip Dec 1999

Page 13: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Set

• List of sorted elements • Fast retrieval based on key (log N)• Fast insert/erase (log N)• Red-black tree (balanced 2-3-4 tree)

•GDC Roadtrip Dec 1999

Page 14: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Map

• Associative array• Dictionary of sorted elements• List of sorted key and value pairs• Same implementation and characteristics of

set container

•GDC Roadtrip Dec 1999

Page 15: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Container Adaptors

• Example adapator code

stack<int, deque<int> > TechnoTrousers;TechnoTrousers.push(1);int i = TechnoTrousers.top();TechnoTrousers.pop();

•GDC Roadtrip Dec 1999

Adaptor Example containers Default container stack list, deque, vector deque queue list, deque deque priority_queue list, deque, vector vector

Page 16: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Iterators

• Typical iterationc<T>::iterator i;

for (i = c.begin(); i != c.end(); ++i) // forward T t = *i;

for (i = c.rbegin(); i != c.rend(); ++i) // reverse T t = *i;

•GDC Roadtrip Dec 1999

Type Valid expressions Example Input *t, ++i, i++, *i++ find() (read-only) Output *x = t, *x++ = t, ++x, x++ insert_iterator<C> (write-only) Forward ++i, i++, *x=t slist<T>::iterator (SGI-specific) Bidirectional ++i, i++, --i, i--, *x=t list<T>::iterator

Random Access

Bidrectional i+=n, i+n, i-=n, i–n, i[n], i[n]=t

vector<T>::iterator, deque<T>::iterator

Page 17: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Algorithms

• Approx. 60 standard algorithms– searching e.g. find()– sorting e.g. sort()– mutating e.g. transform()– numerical e.g. accumulate()

• Most functions take the form:– fn(c.begin(), c.end(), ...)

•GDC Roadtrip Dec 1999

Page 18: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Algorithms (cont.)

• Examples:#include <algorithm>

// return num elements equal to 123int i = count(c.begin(), c.end(), 123);

// negate all elementstransform(c.begin(), c.end(), negate<int>());

// print all elementsfor_each(c.begin(), c.end(), print<int>());

// shuffle the deckrandom_shuffle(deck.begin(), deck.end());

•GDC Roadtrip Dec 1999

Page 19: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Function Objects (Functors)

• C++ objects that can be called like a function to implement “callbacks”

• Use C++ operator()(...)• Simplest type is a function pointer

bool StlStrComp(const char* a, const char* b) { return (strcmp(a, b) == -1); } ( < 0 )??

vector<char*> v;sort(v.begin(), v.end(), StlStrComp);

•GDC Roadtrip Dec 1999

Page 20: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Functors (cont.)

• Functors that do ordering are called “predicates”

struct StlStrPred // “public” class{ bool operator()(const char* a, const char* b) { return (strcmp(a, b) == -1); } ( < 0 )};

vector<char*> v;sort(v.begin(), v.end(), StlStrPred());

•GDC Roadtrip Dec 1999

Page 21: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Efficiency

• Designed to be as fast as hand-coded routines• Limiting factor is typically copy ctor

(constructor) and assignment operator

•GDC Roadtrip Dec 1999

Page 22: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Efficiency (cont.)

• STL faster in some cases than standard C functions

const char* WestWallaby = “Gromit”;strchr(WestWallaby, ‘m’); //cfind(WestWallaby, WestWallaby+6, ‘m’); //c++

•GDC Roadtrip Dec 1999

Page 23: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Efficiency (cont.)

• Sorting (ints)

int arr[nElements];qsort(arr, nElements, sizeof(int), IntComp);

int arr[nElements];sort(arr, arr + nElements); // STL int array sort

vector<int> v;sort(v.begin(), v.end()); // STL int vector sort

•GDC Roadtrip Dec 1999

Page 24: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Efficiency (cont.)

• Sorting (strings)

char* arr[nElements];qsort(arr, nElements, sizeof(char*), StrComp);

sort(arr, arr + nElements, StlStrComp);

sort(v.begin(), v.end(), StlStrComp); // char*

sort(v.begin(), v.end(), StlStrComp); // string

•GDC Roadtrip Dec 1999

Page 25: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

STL Allocators

• Every STL container takes an allocator object as a template parameter

template <class T> public AllocSpecialCheese{ public: pointer allocate(size_type, const void*); void deallocate(void*, size_type); // ... other boilerplate code here};

set<int> Camembert; // default allocator

// All Wensleydale allocations use special allocatorset<int, AllocSpecialCheese> Wensleydale;

•GDC Roadtrip Dec 1999

Page 26: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Template Partial Specialization// generic template function for swapping objectstemplate <class T> void swap(T& x, T& y) { T z(x); x = y; y = z; }

swap(v1, v2); // swapping vectors: slow!v1.swap(v2); // swapping vectors: fast!

// template partial specializationtemplate <class T> void swap(vector<T>& x, vector<T>& y) { x.swap(y); }

swap(v1, v2); // fast!

•GDC Roadtrip Dec 1999

Page 27: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Template Specialization part II // STL generic copy() algorithmtemplate<class InItr, class OutItr> OutItrcopy(InItr bg, InItr end, OutItr val){ for (; bg != end; ++val, ++bg) *val = *bg; return (val); }

// A fast version for simple memory chunkstemplate<> char* copy(const char* bg, const char* end, char* val){ size_t n = end - bg; memcpy(val, bg, n); return (val + n); }

•GDC Roadtrip Dec 1999

Page 28: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Exception Safety

• C++ standard requires the following– destructors may not throw exceptions– valid iterator operations may not throw exceptions– containers must “survive” exceptions; content

unspecified, but still destructable– an exception thrown while inserting one element

leaves the container unchanged– an exception thrown while inserting two+

elements leaves a list unchanged

•GDC Roadtrip Dec 1999

Page 29: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Code Bloat

• Templates expand into different sets of code for each type T

• If different types have the same size and same comparison functions, the compiler can optimize

•GDC Roadtrip Dec 1999

Page 30: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Extending the STL

• Designed for extension• Not difficult to write new algorithms,

containers, or iterators• SGI implementation has many useful

container extensions (hash tables, slist)

•GDC Roadtrip Dec 1999

Page 31: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Common Mistakes (cont.)

• Sorting problems– e.g. lookups fail, a set gets duplicates

• Usually a problem with op < or op ==• Rules

– if x<y is true, y>x is always true– if x<y && y<z are true, x<z is always true– if x==y, then x<y is always false– if !(x<y) and !(y<x), x == y

•GDC Roadtrip Dec 1999

Page 32: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Hiding the Angle Brackets

• Not pretty

// This is hard to readmap<string, int> Shaun;Shaun.insert(pair<string, int>(“abc”, 31));map<string, int>::iterator i = Shaun.find(“abc”);pair<string, int> pr = *i;

pr.first; // “abc”pr.second; // int

•GDC Roadtrip Dec 1999

Page 33: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Hiding the Angle Brackets (cont.)

• Typedefs are your friend

// Tuck these away in a header filetypedef map<string, int> ShaunMap;typedef pair<string, int> ShaunPair;typedef ShaunMap::iterator ShaunItr;

// Same code, but no more angle bracketsShaunMap Shaun;Shaun.insert(ShaunPair(“abc”, 31));ShaunItr i = Shaun.find(“abc”);ShaunPair pr = *i;

•GDC Roadtrip Dec 1999

Page 34: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Vector Tips

• Use reserve() to set aside space when vector size is known in advance

• Can I safely take the address of a vector element, e.g. &v[21]?– According to Standard, no. According to practice,

yes. Standard expected to adjust.

• Trimming unused space

v.swap(vector<T>(v)); // vector, swap thyself!

•GDC Roadtrip Dec 1999

Page 35: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Copying Containers

• The wrong way; copy() can’t add elems

copy(v.begin(), v.end(), nv.begin()); // uh-oh

• Better and best

nv.resize(v.size()); // size of nv matches vcopy(v.begin(), v.end(), nv.begin());

copy(v.begin(), v.end(), back_inserter(nv));copy(m.begin(), m.end(), insert_iterator<T> /* map and set use this method */ (nm, nm.begin());

•GDC Roadtrip Dec 1999

Page 36: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Algorithms that Remove Elems

• Algorithms by themselves can’t insert or delete elements, so they move them!

• unique() moves unique elems to the front and returns an iter to the new “end” of the container

• remove() similarly moves the “unremoved” elems to the front and returns an iter to the new “end”

•GDC Roadtrip Dec 1999

Page 37: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Removing Elems (cont.)

• To actually get rid of the extra elems, you must call erase()

// Removes duplicates and shrinks the containerv.erase(unique(v.begin(), v.end()), v.end());

// Removes the given elements and shrinks vv.erase(remove(v.begin(), v.end(), 1), v.end());

•GDC Roadtrip Dec 1999

Page 38: Embracing the C++ STL: Why Angle Brackets are Good for You Pete Isensee GDC Roadtrip Dec 1999

Vectors vs C-style arrays

• Prefer vector or deque over arrays• Don’t have to know size in advance• Don’t have to keep track of size separately• Little loss of efficiency• Vector works well with legacy code that

expects arrays

•GDC Roadtrip Dec 1999