writing modern c++

53
Writing Modern C++ Marc Grégoire Software Architect marc.gregoire@nuonsoft. com http://www.nuonsoft.com / http://www.nuonsoft.com /blog April 3 rd 2012

Upload: admon

Post on 24-Feb-2016

27 views

Category:

Documents


0 download

DESCRIPTION

Writing Modern C++. Marc Grégoire Software Architect [email protected] http://www.nuonsoft.com / http://www.nuonsoft.com/blog /. Agenda. Why C++? C++ Core Value Modern C++ Resources. Why C++?. C++ is having a kind of renaissance People are coming back to C++ - PowerPoint PPT Presentation

TRANSCRIPT

Page 2: Writing Modern C++

Agenda Why C++? C++ Core Value Modern C++ Resources

Page 3: Writing Modern C++

Why C++? C++ is having a kind of renaissance People are coming back to C++ Main reason: performance / € You want to use the hardware as efficient as

possible and squeeze the most out of it Mobile devices: have limited power, use it

efficiently Datacenters: reducing power requirements

directly results in saving money

Page 4: Writing Modern C++

C++ Core Value Efficient abstraction

Strong abstraction: Type-safe OO and templates for powerful modeling, without sacrificing control and efficiency

Full control over code execution and memory: you can always express what you want to do you can always control memory and data layout exactly, if you

want to Pay-as-you go efficiency

no mandatory overheads, don’t pay for what you don’t use Example: just because C++ supports virtual functions, you don’t

pay a penalty for their support if you don’t use them

Page 5: Writing Modern C++

C++ Core Value Cross platform, cross compiler, cross

operating system Performance very important“It’s incredibly important for C++ to be the language of performance. If there is a language lower than C++ and that has more performance, we didn’t do our job as a C+

+ community.” – Herb Sutter

Page 6: Writing Modern C++

C++ Core Value“The going word at Facebook is that ‘reasonably

written C++ code just runs fast,’ which underscores the enormous effort spent at optimizing PHP

and Java code.

Paradoxically, C++ code is more difficult to write than in other languages, but efficient code is a lot easier.”

– Andrei Alexandrescu

Page 7: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 8: Writing Modern C++

Clean, Safe, Fast Modern C++ code can be Clean, Safe, and

Fast Clean: express with minimal lines of code what

you want to do, just as in other modern languages, resulting in easy to read and easy to understand code

Safe: modern C++ code is exception safe, memory safe, …

Fast: because it’s C++

Page 9: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 10: Writing Modern C++

Things to Unlearn If you have used C++ before, you might have to

unlearn a couple of things Avoid low-level pointer manipulation and raw

memory manipulation, in favor of higher level constructs

Do not use delete / delete [], use smart pointer; it’s: Exceptions safe Leak free Deterministic, unlike garbage collectors

Page 11: Writing Modern C++

Things to Unlearn Never do something like:

FILE* f = fopen("data.ext", "w");// ...fclose(f);

Not exception safe! Use RAII (Resource Acquisition Is Initialization)

Write a wrapper class: Constructor opens the file Destructor automatically closes the file

Often you can use std::shared_ptr, even for the above example

Deterministic

Page 12: Writing Modern C++

Things to Unlearn Instead of:

FILE* f = fopen("data.ext", "w");// ...fclose(f);

Use:shared_ptr<FILE> filePtr(fopen("data.ext", "w"), fclose);

Or write your own wrapper

Page 13: Writing Modern C++

Things to Unlearn Avoid the old C-style algorithms, instead, use

modern C++ algorithms

Page 14: Writing Modern C++

Things to Unlearn For example, qsort() is a C-style algorithm

with following signature:

void qsort (void *base, size_t num, size_t size, int (*comparator) (const void *, const void *));

// Call it as follows for a double arrayqsort(myDoubleArray, n, sizeof(double), compare_double);

Memory to be sorted

Number of elements in

memory

Number of bytes in one

element

Comparison function to

compare two elements

Page 15: Writing Modern C++

Things to Unlearn

Use C++ algorithms like std::sort()Example: std::sort(begin(vec), end(vec));

Side-note:Even though

std::sort() is a higher level construct, it’s

faster than qsort by a large factor (not just

a few percent)

Page 16: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 17: Writing Modern C++

Lambda Expressions Syntax

[capture_block](parameters) mutable exception_specification -> return_type { body }

capture block: how to capture variables from enclosing scope parameters (optional): parameter list, just like a function mutable (optional): variables captured by-value are const, mutable makes

them non-const exception_specification (optional): = throw list return_type (optional): the return type; if omitted

If the body of the lambda expression is of the following form:{ return expression; }

the type of expression will become the return_type of the lambda expression.  Otherwise the return_type is void

Page 18: Writing Modern C++

Lambda Expressions Basic example:

int main(){ []{cout << "Hello from Lambda" << endl;}();}

Capture block [ ] captures nothing [=] captures all variables by value [&] captures all variables by reference

[&x] captures only x by reference and nothing else [x] captures only x by value and nothing else [=, &x, &y] captures by value by default, except variables x and y, which are

captured by reference [&, x] captures by reference by default, except variable x, which is captured by value

Page 19: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 20: Writing Modern C++

Old C++ Versus New C++Oldcircle* p = new circle(42);vector<shape*> vw = load_shapes();for (vector<circle*>::iterator i = vw.begin(); i != vw.end(); ++i){ if (*i && **i == *p) cout << **i << " is a match\n";}for (vector<circle*>::iterator i = vw.begin(); i != vw.end(); ++i){ delete *i;}delete p;

New

auto p = make_shared<circle>(42);vector<shared_ptr<shape>> vw = load_shapes();for_each (begin(vw), end(vw), [&](shared_ptr<circle>& s) { if (s && *s == *p) cout << *s << " is a match\n"; });

T* shared_ptr<T>

new make_shared

no need for “delete”automatic lifetime

managementexception-safe

for/while/do std:: algorithms

[&] lambda functions

auto type deduction

not exception-safe

missing try/catch, __try/__finally

Page 21: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 22: Writing Modern C++

Avoid Delete Write your code in such a way that there is

never a need to use delete or delete[]

DELET

E

Page 23: Writing Modern C++

Avoid Delete Don’t write code as follows:

void foo(){ MyObject* p = new MyObject(); // ... delete p;} It’s not exception safe!

Page 24: Writing Modern C++

Avoid Delete Instead use shared_ptr or unique_ptr:

void foo(){ unique_ptr<MyObject> p = new MyObject(); // ...}

Or, even better, just do:void foo(){ MyObject obj; // ...}

Page 25: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 26: Writing Modern C++

Automatic Lifetime Automatic Lifetime = Efficient + Exception

Safeclass widget {private: gadget g;public: void draw();};

void f() { widget w; /* ... */ w.draw(); /* ... */}lifetime

automatically tied to enclosing

objectno leak, exception

safe

lifetime automatically tied to enclosing scope

constructs w, including the w.g gadget memberAutomatic destruction and

deallocation for w and w.g

Automatic exception safety, as if “finally

{ w.g.dispose(); w.dispose(); }”

Page 27: Writing Modern C++

The Heap and Smart Pointersclass gadget;class widget {private: shared_ptr<gadget> g;};class gadget {private: weak_ptr<widget> w;};

shared ownership

keeps gadget alive w/auto lifetime

mgmtno leak, exception

safe

use weak_ptr to break reference-

count cycles Side-note:Never use the old auto_ptr, it’s officially deprecated!

Page 28: Writing Modern C++

The Heap and Smart Pointersclass node { vector<unique_ptr<node>> children; node* parent; /* … */public: node( node* parent_) : parent(parent_) { children.push_back( new node(…) ); /* … */ }};

unique ownership

node owns its children

no leak, exception safe

node observes its parent

plain “new” should immediately initialize

another object that owns it, example unique_ptr or

shared_ptr

Page 29: Writing Modern C++

C++ and Garbage Collection

“C++ is the best language for garbage collection

principally because it creates less garbage.”

— Bjarne Stroustrup

Page 30: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 31: Writing Modern C++

Containers Avoid using C-style arrays Instead use modern constructs such as STL

containers std::vector std::array std::map std::unordered_map std::multimap std::unordered_multima

p

std::set std::unordered_set std::multiset std::unordered_multiset std::list std::forward_list

Your default container: vector

compact, efficient: cache-friendly,

prefetcher-friendly

fixed size vector

Key-value-pairs: map (tree) or

unordered_map (hash)

set: Like map, but only keys

Page 32: Writing Modern C++

Containers - Examplesvector<string> v;v.push_back("Geddy Lee");

array<int, 50> a;a[0] = 123;

map<string, string> phone;phone["Alex Lifeson"] = "+1 (416) 555-1212";

multimap<string, string> phone;phone["Neil Peart"] = "+1 (416) 555-1212";phone["Neil Peart"] = "+1 (905) 555-1234";

unordered_map<string, string> phone;phone["Alex Lifeson"] = "+1 (416) 555-1212";

unordered_multimap<string, string> phone;phone["Neil Peart"] = "+1 (416) 555-1212";phone["Neil Peart"] = "+1 (905) 555-1234";

Page 33: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 34: Writing Modern C++

LoopsOldfor (auto i = v.begin(); i != v.end(); ++i){ /* ... */}

auto i = v.begin();for (; i != v.end(); ++i){ if (*i > x && *i < y) break;}

Newfor_each (begin(v), end(v), [](string& s) { /* ... */ });

auto i = find_if(begin(v), end(v), [=](int i) { return i > x && i < y; });

for/while/do std:: algorithms

[&] lambda functions

for_each to visit each element

find_if to find a match

prefer non-member

begin()/end()

arbitrary length lambda bodies, just put the loop body inside the lambda

Page 35: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 36: Writing Modern C++

Algorithms Don’t write your own algorithms unless you have a

good reason or there is no standard algorithm for your use-case

Prefer standard algorithms for_each(): Your default traversal algorithm

transform() for not-in-place semantics find_if(): Your default search algorithm sort(), lower_bound(), …: Your default sorting and

searching

Page 37: Writing Modern C++

Algorithms Utility Algorithms

min(), max(), minmax(), swap() minmax() example (C++11)int x1 = 2, x2 = 9, x3 = 3, x4 = 12;pair<int,int> p1 = minmax({x1,x2,x3,x4});cout << "Minmax of 4 elements is " << p1.first << "," << p1.second << endl;

Page 38: Writing Modern C++

Algorithms Nonmodifying algorithms

Search algorithms find(), find_if(), find_if_not(), find_first_of(), search_n(),

lower_bound(), upper_bound(), equal_range(), minmax_element(), all_of(), any_of(), none_of(), …

Numerical processing algorithms count(), count_if(), accumulate(), inner_product(), …

Comparison algorithms equal(), mismatch(), …

Operational algorithms for_each()

Page 39: Writing Modern C++

Algorithms Nonmodifying algorithms examples // Find min and max with 1 algorithm

auto minmax = minmax_element(begin(vec), end(vec));cout << "Min = " << *(minmax.first) << " and Max = " << *(minmax.second) << endl;

// Find the first subsequence of two consecutive 8sauto it = search_n(begin(vec), end(vec), 2, 8);

// all_of()vector<int> vec = {1,1,1,1};bool b = all_of(begin(vec), end(vec), [](int i){return i == 1;});

Page 40: Writing Modern C++

Algorithms Numerical processing algorithms examples // Calculate arithmetic mean of the elements in a vector

double sum = accumulate(begin(vec), end(vec), 0);double avg = sum / vec.size();

// Calculate geometric mean of the elements in a vectordouble mult = accumulate(begin(vec), end(vec), 1, [](int num1, int num2){return num1 * num2;});double geomean = pow(mult, 1.0 / vec.size());

// Create a vector with values 5 6 7 8 9 10 11 12 13 14vector<int> vec(10);iota(begin(vec), end(vec), 5);

Page 41: Writing Modern C++

Algorithms Modifying algorithms

transform(), copy(), copy_if(), move(), swap_ranges(), replace(), replace_if(), fill(), generate(), remove(), remove_if(), reverse(), rotate(), next_permutation(), …

Page 42: Writing Modern C++

Algorithms Modifying algorithms examples // Add 100 to each element in the vector

transform(begin(vec), end(vec), begin(vec), [](int i){return i + 100;});

// Replace all values < 0 with 0replace_if(begin(vec), end(vec), [](int i){return i < 0;}, 0);

// Remove all empty strings from a vector of strings// (Use remove-erase pattern!)auto it = remove_if(begin(strings), end(strings), [](const string& str){return str.empty();});// erase the removed elementsstrings.erase(it, strings.end());

Page 43: Writing Modern C++

Algorithms Sorting algorithms

sort(), stable_sort(), partial_sort(), merge(), … Set algorithms

set_union(), set_intersection(), set_difference(), set_symmetric_difference(), …

Page 44: Writing Modern C++

Algorithms Sorting algorithms example

If you want to do some binary search (lower_bound, upper_bound, equal_range, …), the sequence should be sorted first

Be sure to sort the sequence with the same predicate as you give to the search algorithm

Use named lambda, example:auto comp = [](const widget& w1, const widget& w2) { return w1.weight() < w2.weight(); }

sort(begin(v), end(v), comp);auto i = lower_bound(begin(v), end(v), w, comp);

Page 45: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 46: Writing Modern C++

Move Semantics C++11 Move Semantics increases

efficiency and results in cleaner, easier to understand code

Why is move semantics useful?

Page 47: Writing Modern C++

Move Semantics – Why useful?set<widget> load_huge_data() { set<widget> ret; // … load data and populate ret … return ret;}

widgets = load_huge_data();

vector<string> v = AMillionStrings();

v.insert(begin(v)+v.size()/2, "tom");v.insert(begin(v)+v.size()/2, "richard");v.insert(begin(v)+v.size()/2, "harry");

HugeMatrix operator+( const HugeMatrix&, const HugeMatrix&);

hm3 = hm1+hm2;

efficient, no deep copy

no need for “heap allocation + return a pointer” workaround

efficient, no deep copy-shuffle

(just 1.5M ptr/len assignments)

efficient, no extra copies

Page 48: Writing Modern C++

Move Semantics – How To Implement?class my_class{ unique_ptr<BigHugeData> data;

public: /* ... */

my_class(my_class&& other) : data(move(other.data)) { }

my_class& operator=(my_class&& other) { data = move(other.data); }

void method() { if (!data) throw "moved-from object"; }};

check (if appropriate)

Needs: Move constructor Move assignment operator

Page 49: Writing Modern C++

Move Semantics – When? If you have a copy constructor or copy

assignment operator: Also implement move versions if they can be cheaper than a deep copy

Some types have only move versions, and no copy versions For example: some types are naturally move-

only, such as unique_ptr

Page 50: Writing Modern C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Page 51: Writing Modern C++

Compile Time Encapsulation? Use the Pimpl idiom to truly hide private members

class my_class { // ... all public and protected stuff goes here ...private: class impl; unique_ptr<impl> pimpl; // opaque type here};

class my_class::impl { // defined privately here // ... all private data and functions: all of these // can now change without recompiling callers ...};my_class::my_class() : pimpl(new impl){ /* ... set impl values ... */}

Avoids rebuild cascades Most appropriate for types used often

my_class.h

my_class.cpp

Page 52: Writing Modern C++

Resources “Writing modern C++ code: how C++ has evolved over

the years” – Herb Sutter http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T

“GoingNative 2012, Keynote: C++ Style” – Bjarne Stroustrup http://

channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style

Presentations from GoingNative 2012 http://channel9.msdn.com/Events/GoingNative/GoingNative-2012

Professional C++, 2nd Edition http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470932449.html

Page 53: Writing Modern C++

Questions

?I would like to thank Herb Sutter

from Microsoft for his permission to base this presentation on one that

he wrote for Build 2011.