
Efficient C++ Programming Tips and History Insights" (60 characters)
Explore advanced C++ programming techniques and delve into the history of the language. Learn about implicit variable declaration, range-based loops, smart pointers, and more. Discover the evolution of C++ and its significance in the world of programming. (292 characters)
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
ADRIAN BULAT Enthusiastic Computer Science Researcher & Delusional Programmer PhD Student at University of Nottingham adrian.bulat@nottingham.ac.uk https://www.adrianbulat.com
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
Why C++ ? A glance into the past New standards are/will be released Bjarne Stroustrup Creates C++ while working on his PhD thesis. 2017 2011 2014 1998 1983 1989 1979 2003 1970 Denis Ritchie Creates C language at Bell Labs C++ becomes C++ C++ 2.0 is released
C++1y [](){}();
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. constchar * c = "sample text"; print ( const_cast<char *> (c) );
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); }
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; }
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 (autoconst &outer_iter : StudentGrades) { std::wcout << outer_iter.first << std::endl; }
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;
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
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);
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(); // 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(); }
Range-for statement >> Foloseste un iterator ce este cache-uit >> Utilizeaza pre-incrimentare >> Dereferentiaza iteratorul o singura data for (autoconst &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 (constauto x : { {1,2,3,5,8,13,21,34} }) cout << x << '\n';
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
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 int a(1); int b(); int b(foo); >> Poate genera confuzii si e mai greu de retinut!
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) } >> 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
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; }); } [&] 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]
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(); 0 10 >> Tipul unei expresii lambda >> Nu este std::function void (*foo)(bool, int); foo = [](bool, int){};
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](){};
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()); int x; int& getRef () { return x; } getRef() = 4;
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 ); }
Rvalues and Lvalues C++98 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){ cout << str; } string me( "adrian" ); printReference( me ); printReference( getName() );
Rvalues and Lvalues class ArrayWrapper { public: ArrayWrapper (int n) : _p_vals( newint[ n ] ) , _size( n ) {} ~ArrayWrapper () { delete [] _p_vals; } private: int *_p_vals; int _size; };
Rvalues and Lvalues // copy constructor ArrayWrapper (const ArrayWrapper& other) : _p_vals( newint[ 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; }
Rvalues and Lvalues std::move( other._a_cool_object )
Constexpr constexpr int multiply (int x, int y) { return x * y; } // the compiler may evaluate this at compile time constint val = multiply( 10, 10 ); constexpr int getDefaultArraySize (int multiplier) { return 10 * multiplier; } int my_array[ getDefaultArraySize( 3 ) ];
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? #include <memory>
smart pointers shared_ptr and weak_ptr >> Toti pointerii partajeaza ownershipul obiectului in cauza >> Oricare din pointeri poate pastra obiectul A B ap ap ap ptr ptr ptr ptr ptr ptr ap X1 X2 X3 ap ap
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
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);
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; }
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; } Thing * raw_ptr = sp.get(); // you must want it, but why?
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>());
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
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; } bool is_it_there(weak_ptr<Thing> wp) { if(wp.expired()) { cout << "The Thing is gone!" << endl; returnfalse; } returntrue; }
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 >> pentru a fi folosit ca argument al unei functii trebuie trimis prin referinta
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
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;
smart pointers in trouble void StartJob() { shared_ptr pAircraft(new Aircraft("F-16")); Aircraft* myAircraft = pAircraft.get(); delete myAircraft; }
smart pointers in trouble void StartJob() { shared_ptr ppAircraft(new Aircraft[3]); } void StartJob() { shared_ptr ppAircraft(new Aircraft[3], [](Aircraft* p) { {delete[] p; }); }
smart pointers in trouble int main() { unique_ptr myAircraft = make_unique("F-22"); Aircraft* rawPtr = myAircraft.release(); return 0; }
Syntax sugar >> Digit separator auto million = 1'000'000; >> [[deprecated]] atribute >> User defined literals Bignum operator"" x(constchar* p) { return Bignum(p); } void f(Bignum); f(1234374754987474874759875497524877072);
Bibliography - Using C++11 s Smart Pointers, David Kieras, University of Michigan - C++11 - the new ISO C++ standard, Bjarne Stroustrup (http://www.stroustrup.com/C++11FAQ.html) - Top 10 dumb mistakes to avoid with C++ 11 smart pointers, Deb Haldar
Multumesc! adrian.bulat@nottingham.ac.uk https://www.adrianbulat.com