Download presentation
Presentation is loading. Please wait.
Published byMelanie Baldwin Modified over 7 years ago
1
ADRIAN BULAT Enthusiastic Computer Science Researcher & Delusional Programmer PhD Student at University of Nottingham
2
C(++)ontent Implicit variable declaration and return (auto), or how to become a lazy programmer in C++ Range based loops Initializers for containers Lambda functions How smart are smart pointers Other small features and syntax sugar
3
Why C++ ? A glance into the past
New standards are/will be released Bjarne Stroustrup Creates C++ while working on his PhD thesis. 2017 1970 1979 1983 1989 1998 2003 2011 2014 Denis Ritchie Creates C language at Bell Labs C++ becomes C++ C is released
4
C++1y [](){}();
5
Casting in C++(reminder)
>> dynamic_cast – poate fi folosit doar cu pointeri sau referinte catre clase >> static_cast – poate efectua atat upcast cat si downcast. Nu efectueaza verificari. Folosit deobicei pentru a inversa o conversie implicita. >> reinterpret_cast – converteste orice tip de pointer catre orice alt tip. Este o simpla copie binara de la un pointer la altul. Nimic nu e verificat. >> const_cast – manipuleaza daca obiectul reprezentat de un pointer este const sau nu. Scopul sau este sa asigure ca rezultatul conversii va indica catre un object complet al timpului de destinatie. In general este de tip upscast, de la derivate la clasa de baza), functonand ca o conversie implicita. Faca si downcast, insa doar pentru cele cu membri virtuali. Daca acea conversie esueaza, returneaza NULL. Poate face cast deasemenea catre void*. Converteste to rvlaue reference, converteste enum catre int/float, converteste orice catre void. Apeleaze un constructor sau operator de conversie. Stiu ca arata drept mar, dar este elefant. Reanalizeaza binary datele. Codul deobicei nu e portabil. Permite apelul unei functii ce astepta un const sau nu. Atentie poate cauza probleme daca pointerul ce se credea const e modificat. const char * c = "sample text"; print ( const_cast<char *> (c) );
6
auto keyword >> Permite deducerea automata a tipului variabilei
auto x = 10; //C++11, explicitly return type float f() { return foo() * 42; } auto f() -> float { return foo() * 42; } //C++14 auto f() { return foo() * 42; } //Multiple return auto g() { while( something() ) { if( expr ) return foo() * 42; } return bar.baz(84); O functionalitate mult asteptata in C++11 este keywordul ”auto”, similar cu “var” in alte limbaje.
7
auto keyword Simiplitate și eficiență
std::map<std::wstring, std::map<std::wstring, int>> StudentGrades; StudentGrades[L"Dorel"][L"Physics"] = 3; StudentGrades[L"Dorel"][L"Chemistry"] = 6; StudentGrades[L"Mirel"][L"Physics"] = 7; StudentGrades[L"Mirel"][L"Chemistry"] = 8; for (std::map<std::wstring, std::map<std::wstring, int>>::iterator outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter) { //Print out the student name std::wcout << outerMap_Iter->first << std::endl; }
8
auto keyword for (std::map<std::wstring, std::map<std::wstring, int>>::iterator outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter) { //Print out the student name std::wcout << outerMap_Iter->first << std::endl; } for (auto outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter) { //Print out the student name std::wcout << outerMap_Iter->first << std::endl; } for (auto const &outer_iter : StudentGrades) { std::wcout << outer_iter.first << std::endl; }
9
auto keyword Forțează inițializarea variabilelor
auto a; // does not compile int a; // ok for the compiler Performanță (evitarea conversiilor implicite) int sum; double a=2.0,b=3.2; sum = a+b; // implicit conversion occurs auto a=2.0,b=3.2; auto sum = a+b;
10
auto keyword >> Utilizari ambiguie
auto foo = std::make_shared<Foo>(); // clear auto foo = blablabla(); // unclear const size_t max_size = 100; for ( auto x = max_size; x > 0; --x ) {} auto ptr = condition ? new class1 : new class2; int& foo(); auto bar = foo(); // int& or int? >> Codul poate deveni ilizibil rapid >> Incurajeaza aparitia programatorilor lenesi
11
auto keyword auto flags = DWORD { FILE_FLAG_NO_BUFFERING };
auto numbers = std::vector<int> {1, 2, 3}; auto event_handle = HANDLE {}; auto is_prime(auto n); auto read_async(auto h, auto count); auto foo(auto,auto);
12
decltype keyword >> Similar cu “typeof” int x = 3;
decltype(x) y = x; // same thing as auto y = x; uint16_t id_ = 65535; decltype(auto) id() { return id_; } auto myid = id(); Fratele bun a lui auto. In tip ce auto iti da voie sa declare o variabila d eun anumit tip, declytype iti permite sa obtii timpul unei variabile sau expresii // before string look_up_a_string_1() { return lookup1(); } String& look_up_a_string_2() { return lookup1(); } // now decltype(auto) look_up_a_string_1() { return lookup1(); } decltype(auto) look_up_a_string_2() { return lookup1(); }
13
Range-for statement >> Foloseste un iterator ce este cache-uit
>> Utilizeaza pre-incrimentare >> Dereferentiaza iteratorul o singura data for (auto const &outer_iter : StudentGrades) { std::wcout << outer_iter.first << std::endl; } void f(vector<double>& v) for (auto x : v) std::cout << x << '\n'; for (auto& x : v) ++x; // using a reference so we can edit it for (const auto x : { {1,2,3,5,8,13,21,34} }) cout << x << '\n'; Pentru a face ca acea colectie sa functioneze cu range-for trebuie sa definim: begin(), end() si un iterator peste elemente.
14
Uniform initializer statement – initializer_list<T>
C++98 std::vector<int> ints; ints.push_back(10); ints.push_back(20); ints.push_back(30); C++1y std::vector<int> ints = {10,20,30} ; >> Nu mai sunt doar pentru vectori! >> Mecanizmul este implimentat via std::initializer_list<T> void f(initializer_list<int>); f({1,2}); f{1,2}; // error: function call ( ) missing
15
Uniform initializer statement
C++98 string a[] = { "foo", " bar" }; std::vector<string> v = { "foo", "bar" }; void f(string a[]); f({ "foo", "bar" }}); int a = 2; // "assignment style" int aa[] = { 2, 3 }; // assignment style with list complex z(1,2); // "functional style" initialization x = Ptr(y); // "f. style" for conversion/cast/construction ok: initialize array variable // error: initializer list for non-aggregate vector // syntax error: block as argument ----- // variable definition // function declaration // variable definition or function declaration int a(1); int b(); int b(foo); >> Poate genera confuzii si e mai greu de retinut!
16
Uniform initializer statement
>> C++1y – permite utilizarea {} pentru toate cazurile de initializare >> Conversii prin aproximare nu sunt premise! void test(double val, int val2) { int x2 = val; char c2 = val2; int x3 {val}; char c3 {val2}; char c4 {24}; char c5 {264}; int x4 {2.0}; // error: no double to int value conversion (good) } // if val==7.9, x2 becomes 7 (bad) // if val2==1025, c2 becomes 1 (bad) // error: possible truncation (good) // error: possible narrowing (good) // OK: 24 can be represented exactly as a char (good) // error (assuming 8-bit chars): 264 cannot be >> Recomandata spre a fi folosita mereu exceptie fiind cand auto este utilizat auto a {10}; // a is an initializer_list<int> auto b = 10; // b is an int
17
lambdas >> Reprezinta un mecanizm de specificare a functiilor obiect vector<int> v = {50, -10, 20, -30}; std::sort(v.begin(), v.end()); // the default sort std::sort(v.begin(), v.end(), [](int a, int b) {{ return abs(a)<abs(b); }}); void f(vector<Record>& v){ vector<int> indices(v.size()); int count = 0; generate(indices.begin(),indices.end(),[&count]() {return count++;}); std::sort(indices.begin(), indices.end(), [&](int a, int b){ return v[a].name<v[b].name; }); } ok: initialize array variable // error: initializer list for non-aggregate vector // syntax error: block as argument [&] – lista de “capturare”, toate variabilele locale sunt trimise prin referentiere [&v] – doar v va fi modificat, [] – nici o variabila nu va fi capturata echivalent pentru captura prin valoare putem folosi: [=] si [=v]
18
lambdas >> Capturare prin valoare vs referinta
int i = 0; auto foo = [i](){ cout << i << endl; }; auto bar = [&i](){ cout << i << endl; }; i = 10; foo(); bar(); 10 >> Tipul unei expresii lambda >> Nu este std::function void (*foo)(bool, int); foo = [](bool, int){};
19
lambdas >> lambda “mutabile”
int i = 1; [&i](){ i = 1; }; // ok, 'i' is captured by-reference. [i](){ i = 1; }; // ERROR: assignment of read-only variable 'i'. [i]() mutable { i = 1; }; // ok. >> implicit, operatorul () este cont >> se comporta ca si clasele! >> Dimensiunea unei expresii lambda auto f1 = [](){}; std::array<char, 100> ar; auto f2 = [&ar](){}; auto f3 = [ar](){};
20
Rvalues and Lvalues (reminder)
>> Lvalues sunt valori/expresii a caror adresa poate fi obinuta. >> Rvalues - restul int x; x = 42; x + 2 = 42; x++ = 42; int& foo(); int* goo(); --foo(); ++(*goo()); Lvalues sunt valori care au o adresa ce poate fi obinuta, si ofera o locatie in memorie (semi)permanenta, restul sunt Rvalue. Rvalue sunt valori temporare //OK Wrong, x+2 returneaza o rvalue Wrong Ok, foo returneaza an lvalue Ok, un pointer dereferentiat este un lvalue Getref returneaza o valoare stocata intro variabila globala int x; int& getRef () { return x; } getRef() = 4;
21
Rvalues and Lvalues - who cares?
22
Rvalues and Lvalues vector<int> doubleValues (const vector<int>& v) { vector<int> new_values; new_values.reserve(v.size()); for (auto itr = v.begin(), end_itr = v.end(); itr != end_itr; ++itr ) new_values.push_back( 2 * *itr ); } return new_values; int main() vector<int> v; for ( int i = 0; i < 100; i++ ) v.push_back( i ); v = doubleValues( v );
23
Rvalues and Lvalues C++98 C++1y const string& name = getName(); // ok
string& name = getName(); // NOT ok C++1y const string&& name = getName(); // ok string&& name = getName(); // also ok! printReference (const String& str){ cout << str; } printReference (String&& str){ string me( "adrian" ); printReference( me ); printReference( getName() ); Nu ar fi grozav daca am putea sti ca o expresie este garantat temporara? Ei bine pentru asta sunt referintele de tip rvalue. In C++03, ai putea realiza un astfel de binding folosind un lvalue reference, insa doar daca e const. Motivul de logica e unul simplu: daca ai putea modifica un obiect temporar ar fi periculos. A const ref in C__ faranteaza ca obiectul temporar nu va fi imediat distruns. In C++11 a fost introdus rvalue reference
24
Rvalues and Lvalues class ArrayWrapper { public: ArrayWrapper (int n)
: _p_vals( new int[ n ] ) , _size( n ) {} ~ArrayWrapper () delete [] _p_vals; } private: int *_p_vals; int _size; }; Cele mai frecvente cazuri de utilizare sunt move constructor si operatorul de atribuire.
25
Rvalues and Lvalues // copy constructor
ArrayWrapper (const ArrayWrapper& other) : _p_vals( new int[ other._size ] ) , _size( other._size ) { for ( int i = 0; i < _size; ++i ) _p_vals[ i ] = other._p_vals[ i ]; } // move constructor ArrayWrapper (ArrayWrapper&& other) : _p_vals( other._p_vals ) , _size( other._size ) { other._p_vals = NULL; other._size = 0; } Cele mai frecvente cazuri de utilizare sunt move constructor si operatorul de atribuire. Trebuie setat pe null dupa atribuire( intreaba de ce dupa is nu inainte de atribuire). Desi pasam un rvalue, membrul ei, obinut ca referinta este defapt un lvalue (!). Astfel incat daca other._p_vals era de tip vector<int> el era defapt lavalue! Cum putem solution asta?
26
Rvalues and Lvalues std::move( other._a_cool_object )
Solutia este data std:move localizat in <utility>.
27
Constexpr constexpr int multiply (int x, int y) { return x * y; }
// the compiler may evaluate this at compile time const int val = multiply( 10, 10 ); constexpr int getDefaultArraySize (int multiplier) { return 10 * multiplier; } int my_array[ getDefaultArraySize( 3 ) ]; Permite anumite calcule sa fie facute in timpul compilarii. Pot fi atribuite doar unei const. Pot contine doar o line de cod. Poate apela in interior doar alte functii constexpr. Memomization
28
smart pointers >> Obiecte ce se comporta ca si pointerii nativi, care isi dealocheaza automat memoria la momentul potrivit. >> unique_ptr, shared_ptr, weak_ptr >> conceptul de ownership >> Cum accesam pointerii smart? Smart pointer contains a built-in pointer, and is defined as a template class whose type parameter is the type of the pointed-to object, so you can declare smart pointers that point to a class object of any type. When it comes to dynamically-allocated objects, we often talk about who "owns" the object. "Owning" something means it is yours to keep or destroy as you see fit. Smart pointers make it easier to implement ownership correctly by making the smart pointer destructor the place where the object is deleted. S #include <memory>
29
smart pointers – shared_ptr and weak_ptr
>> Toti pointerii partajeaza ownershipul obiectului in cauza >> Oricare din pointeri poate pastra obiectul A B ap ptr ptr ap Solutia este data de weak_ptr, care doar “observa” un obiect dar nu ii influenteaza durata de viata. In timp ce shared_ptr pot fi utilizati aproape indentic din punct de vedere sintactic, ca si pointerii native, weak_ptr sunt mult mai limitati. Nu ii poti folosi ca si pe un pointer netivi, defapt nu il poti folosi pentru a referi catre un obiect manageuit deloc. Singurul lucru care il poti face este sa verifici daca obiectul inca exista sau sa contruiesti un shared_ptr din el. X1 X2 X3 ap ap
30
smart pointers – shared_ptr and weak_ptr
Restrictii: >> Pot fi folositi pentru a referi doar la obiecte create cu “new” ce pot fi deallocate cu “delete”. >> A nu se folosi impreuna cu pointerii native pentru a evita probleme precum doubla dealocare. >> Existenta unui singur obiect manager pentru fiecare obiect manageriat 1. No pointing to objects on the function call stack. Trying to delete them will cause a runtime error
31
smart pointers – shared_ptr
class Thing { public: void defrangulate(); }; ostream& operator<< (ostream&, const Thing&); shared_ptr<Thing> find_some_thing(); shared_ptr<Thing> do_something_with(shared_ptr<Thing> p); 1. a function can return a shared_ptr 2. a function can take a shared_ptr parameter by value;
32
smart pointers – shared_ptr
void foo() { shared_ptr<Thing> p1(new Thing); shared_ptr<Thing> p2 = p1; shared_ptr<Thing> p3(new Thing); p1 = find_some_thing(); do_something_with(p2); p3->defrangulate(); cout << *p2 << endl; p1.reset(); p2 = nullptr; } 3. // the new is in the shared_ptr constructor expression: 4. // p1 and p2 now share ownership of the Thing 5. // another Thing 6. // p1 may no longer point to first Thing 7. // call a member function like built-in pointer 8. // reset with a member function or assignment to nullptr: 9. // convert nullptr to an empty shared_ptr, and decrement c
33
smart pointers – shared_ptr
Thing * bad_idea() { shared_ptr<Thing> sp; // an empty smart pointer Thing * raw_ptr = new Thing; sp = raw_ptr; // disallowed - compiler error !!! return raw_ptr; // danger } shared_ptr<Thing> better_idea() { shared_ptr<Thing> sp(new Thing); return sp; } Atribuirea directa nu functioneaza. Mostenirea functioneaza correct fiind in spate implementat prin intermediul unui template Thing * raw_ptr = sp.get(); // you must want it, but why?
34
smart pointers – shared_ptr
>> Conversii shared_ptr<Base> base_ptr (new Base); shared_ptr<Derived> derived_ptr; // Casting derived_ptr = static_pointer_cast<Derived>(base_ptr); shared_ptr<Thing> p(new Thing); shared_ptr<Thing> p(make_shared<Thing>());
35
smart pointers – weak_ptr
>> Nu il “tin in viata” pe obiect, ci doar il “observa” >> Pot fi folositi doar pentru a verifica daca un obiect mai exista si pot oferi un shared_ptr catre el. >> Sunt “idiot proff” Nu ofera nici un operator caracterstic iar functia get() nu exista. >> Poate fi reset folosind reset(), dar nu si prin atribuire cu nullptr shared_ptr<Thing> sp(new Thing); weak_ptr<Thing> wp1(sp); // construct wp1 from a shared_ptr weak_ptr<Thing> wp2; // an empty weak_ptr - to nothing wp2 = sp; // wp2 now points to the new Thing weak_ptr<Thing> wp3 (wp2); // construct wp3 from a weak_ptr weak_ptr<Thing> wp4 wp4 = wp2; // wp4 now points to the new Thing
36
smart pointers – weak_ptr
shared_ptr<Thing> sp2 = wp2.lock(); // get shared_ptr >> Verificarea existentei void do_it(weak_ptr<Thing> wp){ shared_ptr<Thing> sp = wp.lock(); // get shared_ptr if(sp) sp->defrangulate(); do something else cout << "The Thing is gone!" << endl; } Functia lock() examineaza starea obiectului manage-uit si verifica existent obiectului manage-uit. Daca nu exista un share_ptr gol va fi oferit. Functia lock se asigura ca pe durata crearii un nou shared_ptr obbiectul va exista, “blocandul” in existent. bool is_it_there(weak_ptr<Thing> wp) { if(wp.expired()) { cout << "The Thing is gone!" << endl; return false; } return true; }
37
smart pointers – unique_ptr
>> similar cu shared_ptr >> asigura unicitatea (nu pot fi copiati sau atribuiti) unique_ptr<Thing> p1 (new Thing); // p1 owns the Thing unique_ptr<Thing> p2(p1); // error - copy construction is not allowed. unique_ptr<Thing> p3; // an empty unique_ptr; p3 = p1; // error, copy assignment is not allowed De ce sa folosim unique_ptr daca este atat de similar cu shared_ptr? 1. Este atat de simplu incat nu consta nimic sa il folosim (it has a pointer that eyver points to the object or nullptr) in timp ce shared_ptr trebuie sa aloce un obiect manager. 2. Un obiect poate gi detinut doar de un unique_ptr, ceea ce il face foarte diferit de shared_ptr >> pentru a fi folosit ca argument al unei functii trebuie trimis prin referinta
38
smart pointers – unique_ptr
>> Transferul de “ownership” //create a Thing and return a unique_ptr to it: unique_ptr<Thing> create_Thing() { unique_ptr<Thing> local_ptr(new Thing); return local_ptr; // local_ptr will surrender ownership } unique_ptr<Thing> p1(new Thing); // p1 owns the Thing unique_ptr<Thing> p2; // p2 owns nothing // invoke move assignment explicitly p2 = std::move(p1); // now p2 owns it, p1 owns nothing Permite unuia sa reprezinte idea de multipli proprietary potentiale, insa doar unul il poate detine la un moment dat de timp. Poate fi realizat usor cu “move semantic”. Dupa aplicarea constructorului de “move” pointerul predecedent detine “nimic” iar cel nou create pe obiect.
39
smart pointers in trouble
int main() { Aircraft* myAircraft = new Aircraft("F-16"); shared_ptr pAircraft(myAircraft); cout << pAircraft.use_count() << endl; shared_ptr pAircraft2(myAircraft); cout << pAircraft2.use_count() << endl; return 0; } // ref-count is 1 An object should be assigned to a shared_ptr as soon as it is created. The raw pointer should never be used again. It’ll cause an ACCESS VIOLATION and crash the program !!!
40
smart pointers in trouble
void StartJob() { shared_ptr pAircraft(new Aircraft("F-16")); Aircraft* myAircraft = pAircraft.get(); delete myAircraft; } Deleting the raw pointer used by the shared_ptr. The result is an all too familiar ACCESS VIOLATION !
41
smart pointers in trouble
void StartJob() { shared_ptr ppAircraft(new Aircraft[3]); } void StartJob() { shared_ptr ppAircraft(new Aircraft[3], [](Aircraft* p) { {delete[] p; }); } Not using a custom deleter when using an array of pointers with a shared_ptr
42
smart pointers in trouble
int main() { unique_ptr myAircraft = make_unique("F-22"); Aircraft* rawPtr = myAircraft.release(); return 0; } Not deleting a raw pointer returned by unique_ptr.release() The Release() method does not destroy the object managed by the unique_ptr, but the unique_ptr object is released from the responsibility of deleting the object. Someone else (YOU!) must delete this object manually. The following code below causes a memory leak because the Aircraft object is still alive at large once the main() exits. Use reset()
43
Syntax sugar >> Digit separator >> [[deprecated]] atribute
auto million = 1'000'000; >> [[deprecated]] atribute >> User defined literals Bignum operator"" x(const char* p) { return Bignum(p); } void f(Bignum); f( );
44
Bibliography - Using C++11’s Smart Pointers, David Kieras, University of Michigan - C the new ISO C++ standard, Bjarne Stroustrup ( - Top 10 dumb mistakes to avoid with C++ 11 smart pointers, Deb Haldar
45
Multumesc!
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.