Introduction to Effective C++ Programming Kwang Hee Ko December 6, 2010 Modeling and Simulation...

50
Introduction to Effective Introduction to Effective C++ Programming C++ Programming Kwang Hee Ko December 6, 2010 Modeling and Simulation Laboratory School of Mechatronics Gwangju Institute of Science and Technology

Transcript of Introduction to Effective C++ Programming Kwang Hee Ko December 6, 2010 Modeling and Simulation...

Introduction to Effective C++ Introduction to Effective C++ ProgrammingProgramming

Kwang Hee Ko

December 6, 2010

Modeling and Simulation LaboratorySchool of Mechatronics

Gwangju Institute of Science and Technology

IntroductionIntroduction

Don’t have be a “C++ language lawyer” to design a good class. But there do exist rules to follow!!!

ReferencesReferences T. Cargill, C++ Programming Style, Addison-Wesley, New York, 1992. T. Cargill, Exception Handling: A False Sense of Security. C++ Report,

6(9):21-24, 1994. J. Coplien, Advanced C++: Programming Styles and Idions, Addison-

Wesley, New York, 1992. M. A. Ellis and B. Stroustrup, The Annotated C++ Reference Manual,

Addison-Wesley, New York, 1990. S. Meyers, More Effective C++: 35 New Ways to Improve Your Programs

and Designs, Addison-Wesley, New York, 1996. S. Meyers, Effective C++: 50 Specific Ways to Improve Your Programs and

Designs, Addison-Wesley, New York, 2nd Edition, 1998. R. Murray, C++ Strategies and Tactics, Addison-Wesley, New York, 1993. B. Stroustrup, The C++ Programming Language, Addison-Wesley, New

York, 3rd Edition, 1997.

TermsTerms

Declaration vs. definitionDefault constructor(default) copy constructorInitialization vs. assignmentInheritance, polymorphism, OOP, etc.

General GuidelinesGeneral Guidelines

Develop your code under at least two compilers. Improve your understanding of C++. Familiarize yourself with the standard library

(STL). Prefer compile-time and link-time errors to runtime

errors. Pay attention to compiler warnings. Ensure that non-local static objects are initialized

before they are used. Know what functions C++ silently writes and calls.

Minimize “C habits” in your C++ Minimize “C habits” in your C++ ProgramProgram

Prefer const and inline to #define.

Prefer iostream to stdio.h.

Prefer new/delete to malloc/free.

Prefer C++ style comments.

Take Good Care of Memory Take Good Care of Memory ManagementManagement

Use the same form in corresponding uses of new and delete.

Use delete on pointer members in destructors. Be prepared for out-of-memory conditions. Adhere to convention when overloading operator

new and operator delete. Avoid hiding the normal form of new. Overload operator delete if you overload operator

new.

Understanding Understanding The Standard Template LibraryThe Standard Template Library

Containers– A container is an object that holds other objects. You can

add objects to a container and remove objects from it. Standard Library Algorithms Iterators and Allocators

– Iterators : They provide an abstract view of data. They are an abstraction of the notion of a pointer to an element of a sequence.

The element currently pointed to : *, -> operations Point to next element : ++ Equality : ==

– Allocators : They are used to insulate container implementations from details of access to memory.

ContainersContainers

<vector> : one-dimensional array <list> : doubly-linked list <deque> : double-ended queue <queue> : queue

– queue, priority queue <stack> : stack <map> : associative array

– Map, multimap <set> : set

– Set, multiset <bitset> : set of booleans

General Info.General Info.

Sequences – <vector> <list> <deque>

Adapters– <queue> <stack>

Example : Vector 1 (Use of []) Example : Vector 1 (Use of []) #include <iostream>#include <vector>#include <algorithm>

using namespace std;

int main(void){ vector<int> v(5); int i; for(i=0;i<5;i++) { v[i] = i; cout << v[i] << endl; } int sum=0; for(i=0;i<5;i++) sum += v[i]; cout << "Sum : " << sum << endl;}

Example : Vector 2 Example : Vector 2 (Use of Iterators) (Use of Iterators)

#include <iostream>#include <vector>#include <algorithm>using namespace std;int main(void){ vector<int> v(5); int sum; vector<int>::iterator first = v.begin(); vector<int>::iterator last = v.end(); sum = 0; while(first != last) { sum += *first; cout << *first << endl; ++first; } cout << "sum : " << sum << endl;}

Example : Vector 3 Example : Vector 3 (Stack Operation) (Stack Operation)

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

int main(void)

{

vector<int> v(5); int sum;

v.push_back(100);

vector<int>::iterator first = v.begin();

vector<int>::iterator last = v.end();

sum = 0;

while(first != last) {

sum += *first;

cout << *first << endl; ++first;

}

cout << "sum : " << sum << endl;

}

Example : Vector 3 Example : Vector 3 (Various Operations) (Various Operations)

vector<int> v(5);

v.push_back(100); // increase the size of v by one

v.pop_back(); // decrease the size by one

v.back() // extract the last object.

v.erase(iterator);

v.insert(iterator,int); // insert int after what iterator points to

v.size() // current size

v.capacity() //

v.max_size() //

Example : List Example : List (Use of Iterators) (Use of Iterators)

No subscripting : operator[]Bidirectional iteratorAdditional operations

– splice, merge, front, push_front, pop_front– sort, unique

Example : List Example : List (Use of Iterators) (Use of Iterators)

#include <iostream>#include <list>#include <algorithm>using namespace std;int main(void){ list<int> v(5); int sum; list<int>::iterator first = v.begin(); list<int>::iterator last = v.end(); sum = 0; while(first != last) { sum += *first; cout << *first << endl; ++first; } cout << "sum : " << sum << endl;}

#include <iostream>#include <list>using namespace std;typedef list<int> List;typedef list<List> ListofList;void print_list(const List& list1, int id);int main(void){ ListofList listoflist1; for(int i=0;i<3;i++) { List list1; for(int j=0;j<4;j++) list1.push_back(i*4 + j); listoflist1.push_back(list1); } ListofList::iterator it = listoflist1.begin(); ListofList::iterator ie = listoflist1.end(); for(int j=1;it != ie; ++it,++j) { const List &list1 = *it; print_list(list1,j); } return 0;}

Example : List of List 1 Example : List of List 1

Example : List of List 2 Example : List of List 2

void print_list(const List& list1, int id){ cout << "list " << id << " : "; List::const_iterator ils = list1.begin(); List::const_iterator ile = list1.end(); while(ils != ile) cout << *ils++ << ' '; cout << endl;}

Example : Stack Example : Stack

#include <iostream>#include <string>#include <stack>using namespace std;int main(void){ stack<string> s; s.push("banana"); s.push("apple"); cout << s.top() << " " << s.size() << endl; s.pop(); cout << s.top() << " " << s.size() << endl; s.pop(); return 0;}

Example : Queue & Example : Queue & Priority_QueuePriority_Queue

#include <iostream>#include <string>#include <queue>using namespace std;int main(void){ queue<string> s; s.push("banana"); s.push("apple"); cout << s.front() << " " << s.size() << endl; s.pop(); cout << s.front() << " " << s.size() << endl; s.pop(); priority_queue<int> k; k.push(56); k.push(2); k.push(100); k.push(5); cout << k.top() << endl; k.pop(); cout << k.top() << endl; return 0;}

Associative ContainersAssociative Containers

map : a single value is associated with each unique key.

multimap : an associative array that allows duplicate elements for a given key.

set, multiset : degenerate associative arrays in which no value is associated with a key.

mapmap

A sequence of (key, value) pairs that provides for fast retrieval based on the key.

Each key in a map is unique.It provides bidirectional iteratorshash_map : useful for a case that there is no

need to keep the container sorted.

Example : map Example : map #include <iostream>#include <string>#include <map>using namespace std;int main(void){ map<string,int> salary; salary["Steve"] = 2000; salary["Tom"] = 3000; salary["John"] = 4500; salary["Neal"] = 3000; salary["Jane"] = 4000; int total = 0; typedef map<string,int>::const_iterator CI; for(CI p = salary.begin(); p!=salary.end(); ++p) { total += p->second; cout << p->first << '\t' << p->second << '\n'; } cout << "-----------------\n total \t" << total << endl; cout << "Tom's salary : " << salary["Tom"] << endl; cout << "Jane's salary : " << salary.find("Jane")->second << endl; return 0;}

AlgorithmsAlgorithms

STL provides algorithms to serve the most common and basic needs.

Most algorithms operate on sequences using iterators and values.

Function objects – Useful when we want the algorithms to execute

code that we supply.

AlgorithmsAlgorithms

Example for function objectsbool less_than_7(int v)

{ return v < 7; }

Void f(list<int>& c)

{

list<int>::const_iterator p = find_if(c.begin(),c.end(),less_than_7);

}

AlgorithmsAlgorithms

Find, for_each, count, copy, search, sort, binary search, etc.

General GuidelinesGeneral Guidelines

Avoid returning “handles” to internal data.Avoid member functions that return non-

const pointers or references to members less accessible than themselves.

Never return a reference to a local object or to a dereferenced pointer initialized by new with in the function (memory leakage).

Postpone variable definitions as long as possible.

General GuidelinesGeneral Guidelines

Public inheritance models : “isa” relation?– Ex. A bus is a vehicle?

Never redefine an inherited non-virtual function.

Never redefine an inherited default parameter value.

Avoid cast down the inheritance hierarchy.Model “has-a” or “is-implemented-in-

terms-of” through layering.

General GuidelinesGeneral Guidelines

Use multiple inheritance judiciously.

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 1

Base 1

f(); g();

Base 2

f(); h();

Derived

j(); k();

Derived d;

d.g(); // OK

d.h(); // OK

d.f(); // Ambiguous!!!

d.Base1::f(); // OK

d.Base2::f(); // OK

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2

Left

int y;

Right

int z;

Bottom

int a

Default inheritance mechanism -> maintains separate copies of the data members inherited from all base classes.

Top

int x;

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2 (Non-virtual Base class)

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

Top

int x; Top

int x;

Top

int x;

Left

int y;

Right

int z;

Bottom

int a

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2 : Virtual Base Class

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

virtual virtual

class Left::public virtual Top{…}class Right::public virtual Top{…}

Top

int x;Left

int y;

Right

int z;

Bottom

int a

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2 : Virtual Base Class

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

virtual virtual

Inherently ambiguous!!!

Ex) Bottom b;

b.x -> b.Left::x?

b.Right::x?

b.Top::x?

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2 : Virtual Base Class

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

virtual virtual

Assignment for Top::x happens twice.

- Bottom->Left->Top

- Bottom->Right->Top

Ambiguities under Multiple Ambiguities under Multiple InheritanceInheritance

Case 2 : Virtual Base Class

Left

int y;

Right

int z;

Bottom

int a

Top

int x;

virtual virtual

Assignment for Top::x happens twice.

- Bottom->Left->Top

- Bottom->Right->Top

Solution???

General GuidelinesGeneral Guidelines

Use multiple inheritance judiciously. Before using virtual base classes, understand them

thoroughly.– Use an experimental program to understand its

behavior. If a public base class does not have a virtual

destructor, no derived class should have a destructor.

If a multiple inheritance hierarchy has any destructors, every base class should have a virtual destructor.

General GuidelinesGeneral Guidelines

Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.

Prefer initialization to assignment in constructors. List members in an initialization list in the order in

which they are declared. Make sure base classes have virtual destructors. Have operator= return a reference to *this. Assign to all data members in operator=. Check for assignment to self in operator=. Overloading vs. default argument.

General Guidelines : EfficiencyGeneral Guidelines : Efficiency

Don’t guess. Use an execution profiler to isolate performance problems. – g++ : use ‘–pg’. Then use ‘gprof’.– For Visual C++, use professional or enterprise

editions

Optimize your code as much as you can at a source level.– Optimization options (‘-O2’ in g++) provided

by a compiler could be another choice. But it has limitation.

General Guidelines : EfficiencyGeneral Guidelines : Efficiency Consider using lazy evaluation.

– Defer computations until the results of those computations are required.

Understand the origin of temporary objects.– Implicit type conversion– Object return from a function

Overload to avoid implicit type conversions. Facilitate the return value optimization. Consider using ‘op=’ instead of stand-alone ‘op’.

– Provide both in your class. Consider alternative libraries.

Efficiency : ExampleEfficiency : Example

First Design– Number of objects created : 15000001– Elapsed Time : 1.3 sec.

int main(void){ Test_Int a(102); a.report(); cout << a << endl; for(int i=0;i<500000;i++) for(int j=0;j<10;j++) a = a + 1;

a.report();cout << a << endl;a.report(); }

int main(void){ Test_Int a(102); a.report(); const Test_Int one=1; cout << a << endl; for(int i=0;i<500000;i++) for(int j=0;j<10;j++) a = a + one;

Efficiency : ExampleEfficiency : Example

Second Design– Number of objects created :– Elapsed Time :

a.report();cout << a << endl;a.report(); }

int main(void){ Test_Int a(102); a.report(); const Test_Int one=1; cout << a << endl; for(int i=0;i<500000;i++) for(int j=0;j<10;j++) a = a + one;

Efficiency : ExampleEfficiency : Example

Second Design– Number of objects created : 10000002 (67%)– Elapsed Time : 0.74 sec. (57%)

a.report();cout << a << endl;a.report(); }

int main(void){ Test_Int a(102); a.report(); const Test_Int one=1; cout << a << endl; for(int i=0;i<500000;i++) for(int j=0;j<10;j++) a += one;

Efficiency : ExampleEfficiency : Example

Third Design– Number of objects created : – Elapsed Time :

a.report();cout << a << endl;a.report(); }

Test_Int Test_Int::operator+=(const Test_Int& t){ return a+=t.a;}

int main(void){ Test_Int a(102); a.report(); const Test_Int one=1; cout << a << endl; for(int i=0;i<500000;i++) for(int j=0;j<10;j++) a += one;

Efficiency : ExampleEfficiency : Example

Third Design– Number of objects created : 5000000 (57%)– Elapsed Time : 0.43 sec. (58%)

a.report();cout << a << endl;a.report(); }

Test_Int Test_Int::operator+=(const Test_Int& t){ return a+=t.a;}

Efficiency : ExampleEfficiency : Example

Fourth Design– Number of objects created : – Elapsed Time :

Test_Int& Test_Int::operator+=(const Test_Int& t){ add(t); return *this;}

void Test_Int::add(const Test_Int& t){ a += t.a;}

Efficiency : ExampleEfficiency : Example

Fourth Design– Number of objects created : 2 (0.00004%)– Elapsed Time : 0.3 sec. (70%)

Test_Int& Test_Int::operator+=(const Test_Int& t){ add(t); return *this;}

void Test_Int::add(const Test_Int& t){ a += t.a;}

Efficiency : ExampleEfficiency : Example

Fourth Design (relative to the first design)– Number of objects created : Almost nothing.– Elapsed Time : 23%

Test_Int& Test_Int::operator+=(const Test_Int& t){ add(t); return *this;}

void Test_Int::add(const Test_Int& t){ a += t.a;}

General Guidelines : EfficiencyGeneral Guidelines : EfficiencyUnderstand the costs of virtual functions.