2. C++ Avancé




2.11. Nouvelles fonctionnalités du C++11

La version C++11 apporte de nouvelles fonctionnalités.

Pour pouvoir les utiliser il faut compiler avec l'option -std=c++11 sous gcc/g++ 4.7 et supérieures.

2.11.1. Alias de namespace

Alias d'espace de nom permet de créer un raccourci pour décrire un espace de nom plus complexe /

  1. // compile with -std=c++11
  2. #include <iostream>
  3. #include <string>
  4. #include <memory> // for unique_ptr
  5. #include <unordered_map>
  6. #include <string>
  7.  
  8. // ------------------------------------
  9. // 1. type definition
  10. // ------------------------------------
  11. typedef
  12.     std::unique_ptr<std::unordered_map<std::string, std::string>>
  13.     UPtrMapSS;
  14.    
  15. // or
  16.  
  17. using UPtrMapSS =
  18.     std::unique_ptr<std::unordered_map<std::string, std::string>>;
  19.  
  20. // ------------------------------------
  21. // 2. function pointer
  22. // ------------------------------------
  23. typedef void (*FP)(int, const std::string&);
  24.  
  25. using FP = void (*)(int, const std::string&);
  26.  
  27.  
  28. // ------------------------------------
  29. // 3. namespaces
  30. // ------------------------------------
  31.  
  32. namespace bio {
  33.     namespace phylo {
  34.          namespace parsimony {
  35.              int my_constant = 1234;
  36.          }
  37.     }
  38. }
  39.  
  40. namespace pars = bio::phylo::parsimony;
  41.  
  42. // ------------------------------------
  43. // main
  44. // ------------------------------------
  45. int main() {
  46.     std::cout << pars::my_constant;
  47.     std::cout << " or " <<  bio::phylo::parsimony::my_constant << '\n';
  48. }
  49.  
  50.  
  51.    
  52.  

2.11.2. Initialisation {} et ()

Qu'est ce qui distingue les 4 initialisations suivantes ?

int x(0); 
int y = 0; 
int z{ 0 }; 
int z = { 0 };

Afin d'éviter la confusion, C++11 introduit l'initialisation uniforme qui utilise les accolades, on parle également de braced initialization (based on braces) :

2.11.3. L'instruction for in

Une nouvelle syntaxe pour l'instruction for a été introduite et permet de réaliser une énumération sur un conteneur de la STL :

  1. // use g++ -std=c++11 to compile
  2.  
  3. #include <vector>
  4. #include <iostream>
  5. using namespace std;
  6.  
  7. int main() {
  8.  
  9.     vector<int> vect;
  10.  
  11.     for (int i= 0; i < 10; ++i) vect.push_back(i);
  12.  
  13.     int sum1 = 0;
  14.     for (int v : vect) {
  15.         sum1 += v;
  16.     }
  17.  
  18.     // or
  19.     int sum2 = 0;
  20.     for (int i=0; i<vect.size(); ++i) {
  21.         sum2 += vect[i];
  22.     }
  23.  
  24.     // or
  25.     int sum3 = 0;
  26.     vector<int>::iterator it;
  27.     for (it = vect.begin(); it != vect.end(); ++it) {
  28.         sum3 += (*it);
  29.     }
  30.    
  31.     cout << "sum1 = " << sum1 << endl;
  32.     cout << "sum2 = " << sum2 << endl;
  33.     cout << "sum3 = " << sum3 << endl;
  34.     return EXIT_SUCCESS;
  35. }
  36.  

2.11.4. Les améliorations liées à la déclaration de type

C++11 introduit deux qualificateurs/déclarateurs de type :

  1. #include <vector>
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <iterator>
  5. using namespace std;
  6.  
  7.  
  8. int main() {
  9.     vector<int> v1(20);
  10.    
  11.     srand(19702013);
  12.    
  13.     generate(v1.begin(), v1.end(), [](){ return rand() % 20; });
  14.    
  15.     // use of auto
  16.     // auto will deduce vector<int>::iterator
  17.     for (auto it = v1.begin(); it != v1.end(); ++it) {
  18.         cout << (*it) << " ";
  19.     }
  20.     cout << endl;
  21.    
  22.     // USE of decltype
  23.     // declare v2 of type v1 and set v2 = v1;
  24.     decltype(v1) v2 = v1;
  25.     transform(v2.begin(), v2.end(), v2.begin(), [](int x) { return x*x; });
  26.    
  27.     // USE of for in
  28.     // print v2 using for in
  29.     for (int x : v2) {
  30.         cout << x << " ";
  31.     }
  32.     cout << endl;
  33.    
  34.     return 0;
  35. }
  36.  

Attention en C++14 on peut utiliser decltype(auto) pour définir par exemple le type de retour d'une fonction.

2.11.5. emplace_back + move au lieu de push_back

Le C++11 introduit une nouvelle fonctionnalité emplace_back qui permet de créer à la volée un élément et de l'insérer dans un vecteur par exemple. Cette fonctionnalité est intéressante dans le cas d'un vector<T> où T est un type défini par l'utilisateur.

Avec un push_back on est contraint de créer une instance de T puis d'en créer une copie. Avec emplace_back on ne crée qu'une seule instance, il faut alors définir un constructor de type move.

  1. #include <vector>
  2. #include <string>
  3. #include <iostream>
  4. using namespace std;
  5.  
  6. class Person1 {
  7. public:
  8.     string name;
  9.     int age;
  10.  
  11.     Person1() {
  12.         cout << "default constructor Person1" << endl;
  13.         name = "unknown";
  14.         age = -1;
  15.     }
  16.    
  17.     Person1(string n, int a) : name(n), age(a) {
  18.         cout << "construct Person1" << endl;
  19.     }
  20.    
  21.     Person1(const Person1& p) {
  22.         cout << "copy constructor Person1" << endl;
  23.         name = p.name;
  24.         age = p.age;
  25.     }
  26.        
  27.     Person1& operator=(const Person1& p) {
  28.         name = p.name;
  29.         age = p.age;
  30.         cout << "assign Person1" << endl;      
  31.     }
  32.    
  33.     friend ostream& operator<<(ostream& out, Person1& p) {
  34.         cout << p.name << ", " << p.age;
  35.     }
  36. };
  37.  
  38. void test1() {
  39.     vector<Person1> v;
  40.     cout << endl << "PUSH BACK" << endl;
  41.     cout << string(40, '-') << endl;
  42.     cout << "- push back" << endl;
  43.     v.push_back(Person1("Masha", 5));
  44. }
  45.  
  46.  
  47. class Person2 {
  48. protected:
  49.     string name;
  50.     int age;
  51.  
  52. public:
  53.     Person2(string n, int a) : name(n), age(a) {
  54.         cout << "construct Person2 { " << name << ", " << age << " }" << endl;
  55.     }
  56.  
  57.     /**
  58.      * introduction of a move constructor and use of std::move()
  59.      */    
  60.     Person2(Person2&& p) : name(std::move(p.name)), age(p.age) {
  61.         cout << "move Person2  { " << name << ", " << age << " }" << endl;
  62.     }
  63.    
  64.     Person2& operator=(const Person2& p) {
  65.         name = p.name;
  66.         age = p.age;
  67.         cout << "assign Person2  { " << name << ", " << age << " }" << endl;       
  68.     }
  69.  
  70. };
  71.  
  72. void test2() {
  73.     vector<Person2> v;
  74.     cout << endl << "EMPLACE BACK" << endl;
  75.     cout << string(40, '-') << endl;
  76.     v.emplace_back(Person2("Masha", 5));
  77.    
  78. }
  79.  
  80. /**
  81.  * main function
  82.  */
  83. int main() {
  84.     test1();
  85.     test2();
  86.    
  87.     return EXIT_SUCCESS;
  88. }
  89.  
PUSH BACK
----------------------------------------
- push back
construct Person1
copy constructor Person1

EMPLACE BACK
----------------------------------------
construct Person2 { Masha, 5 }
move Person2  { Masha, 5 }

2.11.6. Gestion des pointeurs

Deux classes existent qui permettent de manipuler des pointeurs et garantir leur valididté au cours de l'exécution d'un porogramme. Cela permet notamment d'éviter les fuites mémoires (memory leaks) :

2.11.6.a  Unique

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4.  
  5. int main() {
  6.     unique_ptr<int> p1(new int);
  7.    
  8.     *p1 = 2;
  9.     cout << "p1 = " << *p1 << endl;
  10.    
  11.     // will produce double free or corruption (fasttop) error
  12.     // unique_ptr<int> p2(p1.get());
  13.    
  14.     // use move() to transfer the pointer to a new reference
  15.     unique_ptr<int> p2(std::move(p1));
  16.    
  17.     // will produce segmentation fault because owned by p1
  18.     //cout << "p1 = " << *p1 << endl;
  19.    
  20.     *p2 = 7;
  21.     cout << "p2 = " << *p2 << endl;
  22.    
  23.     // then go back to p1
  24.     p1 = std::move(p2);
  25.     cout << "p1 = " << *p1 << endl;
  26.    
  27.     return 0;
  28. }
  29.  
  30.  
p1 = 2
p2 = 7
p1 = 7

2.11.6.b  Shared

  1. #include <iostream>
  2. #include <memory>
  3. #include <algorithm>
  4. #include <vector>
  5. using namespace std;
  6.  
  7. // ===============================================================
  8. // class used for example purpose and that holds one integer value
  9. // ===============================================================
  10. class A {
  11. protected:
  12.     int value;
  13. public:
  14.     // default constructor
  15.     A() : value(-1) {
  16.         cout << "A::default constructor" << endl;
  17.     }
  18.     // constructor with value
  19.     A(int v) : value(v) {
  20.         cout << "A::constructor with value" << endl;
  21.     }
  22.     // destructor
  23.     ~A() {
  24.         cout << "A::destructor" << endl;
  25.     }
  26.     // redefinition of operator() to access value
  27.     int& operator()(void) {
  28.         return value;
  29.     }
  30.    
  31.     // getter
  32.     int get() { return value; }
  33.    
  34.     // setter
  35.     void set(int v) { value = v; }
  36.    
  37.     operator int() const { return value; }
  38.    
  39. };
  40.  
  41.  
  42.  
  43. // ===============================================================
  44. // example with pointer that refer to one pointer to integer
  45. // we create three shared pointers that are released at the
  46. // end of the subprogram
  47. // ===============================================================
  48. void use_shared_ptr_with_int() {
  49.    
  50.     cout << "======================================" << endl;
  51.     cout << " integers" << endl;
  52.     cout << "======================================" << endl;
  53.    
  54.     shared_ptr<int> p1(new int);
  55.     shared_ptr<int> p2(p1);
  56.     shared_ptr<int> p3(p1);
  57.    
  58.     *p1 = 1;
  59.     *p2 = 2;
  60.     *p3 = 3;
  61.    
  62.     // print number of references to array of integers
  63.     cout << "int is referenced " << p1.use_count() << " time(s)" << endl;
  64.  
  65. }
  66.  
  67. // ===============================================================
  68. // example with array of integers
  69. // we create one shared_ptr that contains a pointer to an array
  70. // of 10 integers
  71. // ===============================================================
  72. void use_shared_ptr_with_int_array() {
  73.    
  74.     cout << "======================================" << endl;
  75.     cout << " array of integers" << endl;
  76.     cout << "======================================" << endl;
  77.    
  78.     // it is necessary to redefine the destructor as a lambda function
  79.     shared_ptr<int> p1(new int[10], [](int *tab) { delete [] tab; } );
  80.        
  81.     // assign each element to a value  
  82.     for (int i=0; i<10; ++i) {
  83.         (static_cast<int *>(p1.get())[i]) = i;
  84.     }
  85.  
  86.     // declare another reference to p1
  87.     shared_ptr<int> p2(p1);
  88.        
  89.     // replace each element by its square value using p2   
  90.     transform(&static_cast<int *>(p2.get())[0],
  91.         &static_cast<int *>(p2.get())[10],
  92.         &static_cast<int *>(p2.get())[0],
  93.         [](int n) { return n*n; });
  94.    
  95.    
  96.     // print each value using p1 (or p2)
  97.     for (int i=0; i<10; ++i) {
  98.         cout << static_cast<int *>(p1.get())[i] << " ";
  99.     }  
  100.     cout << endl;
  101.    
  102.     // print number of references to array of integers
  103.     cout << "Array is referenced " << p1.use_count() << " time(s)" << endl;
  104.    
  105.     // remove reference of p1
  106.     p1 = nullptr;
  107.    
  108.     // print number of references to array of integers
  109.     cout << "Array is referenced " << p2.use_count() << " time(s)" << endl;
  110. }
  111.  
  112. // ===============================================================
  113. // example with array of class instance
  114. // we create one shared_ptr that contains a pointer to an array
  115. // of 10 instances of class A
  116. // ===============================================================
  117. void use_shared_ptr_with_class_array() {
  118.  
  119.     cout << "======================================" << endl;
  120.     cout << " array of class A" << endl;
  121.     cout << "======================================" << endl;
  122.    
  123.     // it is necessary to redefine the destructor as a lambda function
  124.     shared_ptr<A> p1(new A[10], [](A *tab) { delete [] tab; } );
  125.        
  126.     // assign a value to each element using the operator() 
  127.     for (int i=0; i<10; ++i) {
  128.         (static_cast<A *>(p1.get())[i])() = i;
  129.     }
  130.    
  131.     // print value of each element using p1 and operator ()
  132.     for (int i=0; i<10; ++i) {
  133.         cout << (static_cast<A *>(p1.get())[i])() << " ";
  134.     }
  135.     cout << endl;
  136.  
  137.     // declare another reference to p1
  138.     shared_ptr<A> p2(p1);
  139.        
  140.     // replace each value by its square value using p2 and get/set 
  141.     for_each(&static_cast<A *>(p2.get())[0],
  142.         &static_cast<A *>(p2.get())[10],
  143.          [](A& obj) -> void { obj.set( obj.get() * obj.get()); });
  144.          
  145.     // replace each value by its square value using p2 and operator()  
  146.     for_each(&static_cast<A *>(p2.get())[0],
  147.         &static_cast<A *>(p2.get())[10],
  148.          [](A& obj) -> void { obj() = obj() * obj(); });
  149.          
  150.    
  151.     // print each element using p1 (or p2) and operator int
  152.     for (int i=0; i<10; ++i) {
  153.         cout << static_cast<int>(static_cast<A *>(p1.get())[i]) << " ";
  154.     }  
  155.     cout << endl;
  156.    
  157.     // print number of references to array of integers
  158.     cout << "Array is referenced " << p1.use_count() << " time(s)" << endl;
  159.    
  160.     // remove reference of p1
  161.     p1 = nullptr;
  162.    
  163.     // print number of references to array of integers
  164.     cout << "Array is referenced " << p2.use_count() << " time(s)" << endl;
  165.    
  166. }
  167.  
  168. // =================
  169. // main function
  170. // =================
  171. int main() {
  172.     use_shared_ptr_with_int();
  173.     use_shared_ptr_with_int_array();
  174.     use_shared_ptr_with_class_array();
  175.     return 0;
  176. }  
  177.    
  178.  
======================================
 integers
======================================
int is referenced 3 time(s)
======================================
 array of integers
======================================
0 1 4 9 16 25 36 49 64 81 
Array is referenced 2 time(s)
Array is referenced 1 time(s)
======================================
 array of class A
======================================
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
A::default constructor
0 1 2 3 4 5 6 7 8 9 
0 1 16 81 256 625 1296 2401 4096 6561 
Array is referenced 2 time(s)
Array is referenced 1 time(s)
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor
A::destructor

L'utilisation de valgrind permet de vérifier l'absence de fuite mémoire :

\$ valgrind ./c2_shared_ptr.exe
==3476== Memcheck, a memory error detector
==3476== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==3476== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==3476== Command: ./c2_shared_ptr.exe
==3476==
...
==3476== 
==3476== HEAP SUMMARY:
==3476==     in use at exit: 0 bytes in 0 blocks
==3476==   total heap usage: 6 allocs, 6 frees, 180 bytes allocated
==3476== 
==3476== All heap blocks were freed -- no leaks are possible
==3476== 
==3476== For counts of detected and suppressed errors, rerun with: -v
==3476== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Voici un autre exemple à tester :

  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <cstring>
  4. using namespace std;
  5.  
  6. char *str;
  7. int *arr;
  8. float *tab;
  9.  
  10. /**
  11.  * allocate resources
  12.  */
  13. void allocate_data() {
  14.     str = new char [100];
  15.     arr = (int *) malloc(100 * sizeof(int));
  16.     tab = new float[10];
  17. }
  18.  
  19. /**
  20.  * free resources
  21.  */
  22. void free_data() {
  23.     // should be delete [] str
  24.     delete str;
  25.     // should be free(arr);
  26.     delete [] arr;
  27.     // forget to free tab
  28. }
  29.  
  30. int main() {
  31.     allocate_data();
  32.    
  33.     strcpy(str, "toto");
  34.     // use more elements than allocated
  35.     for (int i=0; i<101; ++i) {
  36.         arr[i] = i;
  37.     }
  38.    
  39.     free_data();
  40.    
  41.     exit(EXIT_SUCCESS);
  42. }
  43.  
  44. /**
  45.  * valgrind -v --leak-check=summary --log-file=a.txt ./valgrind_example.exe
  46.  * valgrind -v --tool=cachegrind --log-file=a.txt ./valgrind_example.exe
  47.  *
  48.  */
  49.  
  50. /*
  51. ==6354== Invalid write of size 4
  52. ==6354==    at 0x4008D7: main (valgrind_example.cpp:36)
  53. ==6354==  Address 0x5a1c280 is 0 bytes after a block of size 400 alloc'd
  54. ==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  55. ==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
  56. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  57. ==6354==
  58. --6354-- REDIR: 0x4e940f0 (operator delete(void*)) redirected to 0x4c2c250 (operator delete(void*))
  59. ==6354== Mismatched free() / delete / delete []
  60. ==6354==    at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  61. ==6354==    by 0x40087B: free_data() (valgrind_example.cpp:24)
  62. ==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
  63. ==6354==  Address 0x5a1c040 is 0 bytes inside a block of size 100 alloc'd
  64. ==6354==    at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.
  65. so)
  66. ==6354==    by 0x40083A: allocate_data() (valgrind_example.cpp:14)
  67. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  68. ==6354==
  69. --6354-- REDIR: 0x4e94120 (operator delete[](void*)) redirected to 0x4c2c7d0 (operator delete[](void*))
  70. ==6354== Mismatched free() / delete / delete []
  71. ==6354==    at 0x4C2C83C: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  72. ==6354==    by 0x400896: free_data() (valgrind_example.cpp:26)
  73. ==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
  74. ==6354==  Address 0x5a1c0f0 is 0 bytes inside a block of size 400 alloc'd
  75. ==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  76. ==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
  77. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  78. ==6354==
  79. --6354-- REDIR: 0x51bddf0 (free) redirected to 0x4c2bd80 (free)
  80. ==6354==
  81. ==6354== HEAP SUMMARY:
  82. ==6354==     in use at exit: 40 bytes in 1 blocks
  83. ==6354==   total heap usage: 3 allocs, 2 frees, 540 bytes allocated
  84. ==6354==
  85. ==6354== Searching for pointers to 1 not-freed blocks
  86. ==6354== Checked 193,168 bytes
  87. ==6354==
  88. ==6354== LEAK SUMMARY:
  89. ==6354==    definitely lost: 0 bytes in 0 blocks
  90. ==6354==    indirectly lost: 0 bytes in 0 blocks
  91. ==6354==      possibly lost: 0 bytes in 0 blocks
  92. ==6354==    still reachable: 40 bytes in 1 blocks
  93. ==6354==         suppressed: 0 bytes in 0 blocks
  94. ==6354== Rerun with --leak-check=full to see details of leaked memory
  95. ==6354==
  96. ==6354== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
  97. ==6354==
  98. ==6354== 1 errors in context 1 of 3:
  99. ==6354== Mismatched free() / delete / delete []
  100. ==6354==    at 0x4C2C83C: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  101. ==6354==    by 0x400896: free_data() (valgrind_example.cpp:26)
  102. ==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
  103. ==6354==  Address 0x5a1c0f0 is 0 bytes inside a block of size 400 alloc'd
  104. ==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  105. ==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
  106. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  107. ==6354==
  108. ==6354==
  109. ==6354== 1 errors in context 2 of 3:
  110. ==6354== Mismatched free() / delete / delete []
  111. ==6354==    at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  112. ==6354==    by 0x40087B: free_data() (valgrind_example.cpp:24)
  113. ==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
  114. ==6354==  Address 0x5a1c040 is 0 bytes inside a block of size 100 alloc'd
  115. ==6354==    at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  116. ==6354==    by 0x40083A: allocate_data() (valgrind_example.cpp:14)
  117. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  118. ==6354==
  119. ==6354==
  120. ==6354== 1 errors in context 3 of 3:
  121. ==6354== Invalid write of size 4
  122. ==6354==    at 0x4008D7: main (valgrind_example.cpp:36)
  123. ==6354==  Address 0x5a1c280 is 0 bytes after a block of size 400 alloc'd
  124. ==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
  125. ==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
  126. ==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
  127. ==6354==
  128. ==6354== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
  129.  
  130.  
  131.  
  132.  
  133. */
  134.  
  135.  
\$ valgrind -v --leak-check=summary --log-file=a.txt ./valgrind_example.exe
\$ cat a.txt

==6354== Invalid write of size 4
==6354==    at 0x4008D7: main (valgrind_example.cpp:36)
==6354==  Address 0x5a1c280 is 0 bytes after a block of size 400 alloc'd
==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
==6354== 
--6354-- REDIR: 0x4e940f0 (operator delete(void*)) redirected to 0x4c2c250 (operator delete(void*))
==6354== Mismatched free() / delete / delete []
==6354==    at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6354==    by 0x40087B: free_data() (valgrind_example.cpp:24)
==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
==6354==  Address 0x5a1c040 is 0 bytes inside a block of size 100 alloc'd
==6354==    at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6354==    by 0x40083A: allocate_data() (valgrind_example.cpp:14)
==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
==6354== 
--6354-- REDIR: 0x4e94120 (operator delete[](void*)) redirected to 0x4c2c7d0 (operator delete[](void*))
==6354== Mismatched free() / delete / delete []
==6354==    at 0x4C2C83C: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6354==    by 0x400896: free_data() (valgrind_example.cpp:26)
==6354==    by 0x4008E7: main (valgrind_example.cpp:39)
==6354==  Address 0x5a1c0f0 is 0 bytes inside a block of size 400 alloc'd
==6354==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6354==    by 0x40084E: allocate_data() (valgrind_example.cpp:15)
==6354==    by 0x4008A5: main (valgrind_example.cpp:31)
==6354== 
--6354-- REDIR: 0x51bddf0 (free) redirected to 0x4c2bd80 (free)
==6354== 
==6354== HEAP SUMMARY:
==6354==     in use at exit: 40 bytes in 1 blocks
==6354==   total heap usage: 3 allocs, 2 frees, 540 bytes allocated
==6354== 
==6354== Searching for pointers to 1 not-freed blocks
==6354== Checked 193,168 bytes
==6354== 
==6354== LEAK SUMMARY:
==6354==    definitely lost: 0 bytes in 0 blocks
==6354==    indirectly lost: 0 bytes in 0 blocks
==6354==      possibly lost: 0 bytes in 0 blocks
==6354==    still reachable: 40 bytes in 1 blocks
==6354==         suppressed: 0 bytes in 0 block

2.11.7. Ratio

2.11.8. Gestion du temps

C++11 introduit plusieurs fonctionnalités pour gérer le temps :

Voici une version d'un chronomètre lisible difficilement :

  1. #include <chrono>
  2. #include <iostream>
  3. #include <iomanip>
  4. #include <cmath>
  5. #include <typeinfo>
  6. using namespace std;
  7.  
  8.  
  9. template <typename V, typename R>
  10. ostream& operator << (ostream& s, const chrono::duration<V,R>& d) {
  11.    s << "[" << d.count() << " of " << R::num << "/"
  12.                                    << R::den << "]";
  13.    return s;
  14. }
  15.  
  16. class Chrono {
  17. protected:
  18.     std::chrono::time_point<std::chrono::high_resolution_clock> t_start, t_stop;
  19.    
  20. public:
  21.     Chrono() {
  22.         t_stop = t_start = std::chrono::high_resolution_clock::now();
  23.     }
  24.     void start() {
  25.         t_stop = t_start = std::chrono::high_resolution_clock::now();
  26.     }
  27.     void stop() {
  28.         t_stop = std::chrono::high_resolution_clock::now();
  29.     }
  30.    
  31.     ostream& print(ostream& out) {
  32.         auto ms = t_stop - t_start;
  33.         std::chrono::hours   hh = std::chrono::duration_cast<std::chrono::hours>(ms);
  34.         std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(ms % chrono::hours(1));
  35.         std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(ms % chrono::minutes(1));
  36.         std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(ms % chrono::seconds(1));
  37.         out << setfill('0') << setw(2) << hh.count() << "::"
  38.             << setw(2) << mm.count() << "::"
  39.             << setw(2) << ss.count() << "::"
  40.             << setw(3) << msec.count();
  41.         return out;
  42.     }
  43.    
  44.     ostream& print_raw(ostream& out) {
  45.         auto ms = t_stop - t_start;
  46.         std::chrono::hours   hh = std::chrono::duration_cast<std::chrono::hours>(ms);
  47.         std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(ms % chrono::hours(1));
  48.         std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(ms % chrono::minutes(1));
  49.         std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(ms % chrono::seconds(1));
  50.         std::cout <<  hh << "::" << mm << "::" << ss << "::" << msec << endl;
  51.         return out;
  52.     }
  53.    
  54.    
  55.     friend ostream& operator<<(ostream& out, Chrono& c) {
  56.         return c.print(out);
  57.     }
  58.    
  59.  
  60. };
  61.  
  62. int main()  {
  63.     Chrono chrono;
  64.    
  65.     chrono.start();
  66.    
  67.     for (int r=0; r<700;++r) {
  68.         float sum = 0.0;
  69.         for (int i=0; i<1000000; ++i) {
  70.             sum += sqrt(i);
  71.         }
  72.     }
  73.    
  74.     chrono.stop();
  75.     cout << "duration=" << chrono << endl;
  76.     cout << "duration raw = ";
  77.     chrono.print_raw(cout);
  78.     cout << endl;  
  79.    
  80.     return 0;
  81. }
  82.  

Ainsi qu'une version un peu plus lisible qui utilise l'alias de namespace :

  1. #include <chrono>
  2. #include <iostream>
  3. #include <iomanip>
  4. #include <cmath>
  5. #include <typeinfo>
  6. using namespace std;
  7.  
  8. namespace SC = std::chrono;
  9.  
  10. template <typename V, typename R>
  11. ostream& operator << (ostream& s, const chrono::duration<V,R>& d) {
  12.    s << "[" << d.count() << " of " << R::num << "/"
  13.                                    << R::den << "]";
  14.    return s;
  15. }
  16.  
  17. class Chrono {
  18. protected:
  19.     typedef SC::time_point<SC::high_resolution_clock> Time;
  20.      
  21.     Time t_start, t_stop;
  22.    
  23. public:
  24.     Chrono() {
  25.         t_stop = t_start = SC::high_resolution_clock::now();
  26.     }
  27.     void start() {
  28.         t_stop = t_start = SC::high_resolution_clock::now();
  29.     }
  30.     void stop() {
  31.         t_stop = SC::high_resolution_clock::now();
  32.     }
  33.    
  34.     ostream& print(ostream& out) {
  35.         auto ms = t_stop - t_start;
  36.         SC::hours   hh = SC::duration_cast<SC::hours>(ms);
  37.         SC::minutes mm = SC::duration_cast<SC::minutes>(ms % chrono::hours(1));
  38.         SC::seconds ss = SC::duration_cast<SC::seconds>(ms % chrono::minutes(1));
  39.         SC::milliseconds msec = SC::duration_cast<SC::milliseconds>(ms % chrono::seconds(1));
  40.         out << setfill('0') << setw(2) << hh.count() << "::"
  41.             << setw(2) << mm.count() << "::"
  42.             << setw(2) << ss.count() << "::"
  43.             << setw(3) << msec.count();
  44.         return out;
  45.     }
  46.    
  47.     ostream& print_raw(ostream& out) {
  48.         auto ms = t_stop - t_start;
  49.         SC::hours   hh = SC::duration_cast<SC::hours>(ms);
  50.         SC::minutes mm = SC::duration_cast<SC::minutes>(ms % chrono::hours(1));
  51.         SC::seconds ss = SC::duration_cast<SC::seconds>(ms % chrono::minutes(1));
  52.         SC::milliseconds msec = SC::duration_cast<SC::milliseconds>(ms % chrono::seconds(1));
  53.         std::cout <<  hh << "::" << mm << "::" << ss << "::" << msec << endl;
  54.         return out;
  55.     }
  56.    
  57.    
  58.     friend ostream& operator<<(ostream& out, Chrono& c) {
  59.         return c.print(out);
  60.     }
  61.    
  62.  
  63. };
  64.  
  65. int main()  {
  66.     Chrono chrono;
  67.    
  68.     chrono.start();
  69.    
  70.     for (int r=0; r<700;++r) {
  71.         float sum = 0.0;
  72.         for (int i=0; i<1000000; ++i) {
  73.             sum += sqrt(i);
  74.         }
  75.     }
  76.    
  77.     chrono.stop();
  78.     cout << "duration=" << chrono << endl;
  79.     cout << "duration raw = ";
  80.     chrono.print_raw(cout);
  81.     cout << endl;  
  82.    
  83.     return 0;
  84. }
  85.  
# compilation sans optimisation
\$ g++ -o c2_chrono_v2.exe c2_chrono_v2.cpp -std=c++11 -lm
\$ ./c2_chrono_v2.exe 
duration=00::00::06::918
duration raw = [0 of 3600/1]::[0 of 60/1]::[6 of 1/1]::[918 of 1/1000]

# compilation avec optimisation -O2
\$ g++ -o c2_chrono_v2.exe c2_chrono_v2.cpp -std=c++11 -lm -O2
richer@thor:~/public_html/ens/inra$ ./c2_chrono_v2.exe 
duration=00::00::00::791
duration raw = [0 of 3600/1]::[0 of 60/1]::[0 of 1/1]::[791 of 1/1000]

2.11.9. Nombres aléatoires

C++11 introduit des classes pour la génération de nombres aléatoires ainsi que les traitements liés aux probabilités. Ces classes sont issues de boost.

Pour rappel une séquence de nombre aléatoires est obtenue par une formule de récurrence partant d'une valeur initiale appelée graine (ou seed en anglais).

La bibliothèque <random> introduit :

L'un des éléments importants d'un générateur est la période de la série de nombres générés. Celle ci doit être assez grande pour éviter d'obtenir les mêmes valeurs de manière répétitive. La fonction rand() du C utilise un générateur congruentiel linéaire qui est supposé de mauvaise qualité, on recommande d'utiliser par exemple un générateur de type mersenne twister.

C++11 introduit std::seed_seq pour encourage l'utilisation de meilleurs graines.

Voici un exemple simple pour générer des nombres aléatoires entiers avec distribution uniforme dans un intervalle donné :

  1. #include <random>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <vector>
  5. #include <map>
  6. #include <iterator>
  7. #include <algorithm>
  8. #include <chrono>
  9. using namespace std;
  10.  
  11. const int MAX_ITER = 10000;
  12. const int MIN_VALUE = 1;
  13. const int MAX_VALUE = 2;
  14.  
  15. vector<int> values;
  16.  
  17. void avg_std() {
  18.     int sum = accumulate(values.begin(), values.end(), 0);
  19.     double avg = static_cast<float>(sum) / values.size();
  20.    
  21.     double sq_sum = std::inner_product(values.begin(), values.end(), values.begin(), 0.0);
  22.     double stdev = std::sqrt(sq_sum / values.size() - avg * avg);
  23.     auto itmin = min_element(values.begin(), values.end());
  24.     auto itmax = max_element(values.begin(), values.end());
  25.     cout << "avg=" << avg  
  26.         << ", std=" << stdev
  27.         << ", min=" << (*itmin)
  28.         << ", max=" << (*itmax)
  29.         << endl;
  30.     map<int,int> count;
  31.     for (auto x : values) {
  32.         ++count[x];
  33.     }  
  34.     for (auto y : count) {
  35.         cout << y.first <<": " << y.second << endl;
  36.     }
  37. }
  38.  
  39. // default rand function
  40. void default_c() {
  41.     srand(time(nullptr));
  42.     for (int i=0; i<MAX_ITER; ++i) {
  43.         values.push_back(MIN_VALUE + rand() % (MAX_VALUE - MIN_VALUE + 1));
  44.     }
  45.     avg_std();
  46. }
  47.  
  48. // use of uniform distribution with default random engine
  49. void default_random() {
  50.     default_random_engine generator( time(nullptr) );
  51.     uniform_int_distribution<int> distribution{MIN_VALUE, MAX_VALUE};
  52.    
  53.     values.clear();
  54.     for (int i=0; i<MAX_ITER; ++i) values.push_back( distribution(generator) );
  55.     avg_std();
  56. }
  57.  
  58. // use of uniform distribution with Mersenne Twister
  59. void mt19937_random() {
  60.     unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  61.  
  62.     mt19937 generator(seed);
  63.     uniform_int_distribution<int> distribution{MIN_VALUE, MAX_VALUE};
  64.    
  65.     values.clear();
  66.     for (int i=0; i<MAX_ITER; ++i) values.push_back( distribution(generator) );
  67.     avg_std();
  68. }
  69.  
  70. int main() {
  71.     default_c();
  72.     default_random();
  73.     mt19937_random();
  74.     exit(EXIT_SUCCESS);
  75. }
  76.  

Un des résultats en sortie pour l'intervalle $[1,2]$ avec 10.000 valeurs générées est :

avg=1.4931, std=0.499952, min=1, max=2
1: 5069
2: 4931
avg=1.4957, std=0.499982, min=1, max=2
1: 5043
2: 4957
avg=1.5061, std=0.499963, min=1, max=2
1: 4939
2: 5061

Un des résultats en sortie pour l'intervalle $[1,100]$ avec 10.000 valeurs générées est :

avg=50.0572, std=28.7731, min=1, max=100
avg=50.0615, std=28.8766, min=1, max=100
avg=50.3719, std=28.905, min=1, max=100

Voir également l'article Random number generation using C++ TR1 de John D. Cook.