Outils pour utilisateurs

Outils du site


lang:cpp:classes

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
lang:cpp:classes [2020/05/19 00:20] – [override / redéfinition] : les destructeurs ne doivent pas générer d'exception rootlang:cpp:classes [2025/07/28 13:38] (Version actuelle) – [Version "Rule of Zero"] : clarification du commentaire ''does not have a user-declared'' root
Ligne 1: Ligne 1:
 =====Sans héritage===== =====Sans héritage=====
- 
-====Multiples constructeurs==== 
-Il est possible d'appeler un constructeur depuis un autre constructeur. C'est pratique si on souhaite avoir deux constructeurs très proche. 
- 
-<code cpp> 
-class A{}; 
-class B : public A 
-{ 
- public: 
-  // Call constructor B(int) from constructor B(). 
-  // Dont call the parent A() when calling another constructor of the same class. 
-  B() : B(5){} 
-  // Avoid default function argument 
-  B(int a/* = 5*/):A(), a_(a){} 
- 
- private: 
-  int a_; 
-}; 
-</code> 
  
 ====Version "Rule of Zero"==== ====Version "Rule of Zero"====
Ligne 24: Ligne 5:
 Cette règle s'applique lorsque la classe ne gère pas de ressources. Cette règle s'applique lorsque la classe ne gère pas de ressources.
  
-Il est aussi conseillé de réduire son utilisation aux classes n'étant pas polymorphique pour éviter les problèmes de slicing.+Il est aussi conseillé de réduire son utilisation aux classes n'étant pas polymorphiques (avec une méthode virtuelle) pour éviter les problèmes de slicing.
  
 <code cpp> <code cpp>
Ligne 32: Ligne 13:
   // Constructor.   // Constructor.
   Class() = default;   Class() = default;
-  // Pas de destructeur. Pas de copy/move contructor/assignment operator+  // Pas de définition explicite du destructeur
 +  // ni de copy/move contructor/assignment operator
 } }
 </code> </code>
Ligne 47: Ligne 29:
 <cite>[[https://stackoverflow.com/questions/40959659/c-rule-of-zero-what-is-user-declared-constructor|C++ Rule of Zero & what is "user-declared" constructor?]] {{ :lang:cpp:class:c_rule_of_zero_what_is_user-declared_constructor_-_stack_overflow_2020-03-09_23_01_28_.html |Archive du 04/12/2016 le 09/03/2020}}</cite></blockquote> <cite>[[https://stackoverflow.com/questions/40959659/c-rule-of-zero-what-is-user-declared-constructor|C++ Rule of Zero & what is "user-declared" constructor?]] {{ :lang:cpp:class:c_rule_of_zero_what_is_user-declared_constructor_-_stack_overflow_2020-03-09_23_01_28_.html |Archive du 04/12/2016 le 09/03/2020}}</cite></blockquote>
  
-Par ''does not have a user-declared'', cela signifie aussi ne pas utiliser ''= default;''.+''does not have a user-declared'' signifie également ne pas utiliser ''= default;''.
  
 Autre explication : [[https://www.fluentcpp.com/2019/04/23/the-rule-of-zero-zero-constructor-zero-calorie/|The Rule of Zero in C++]] {{ :lang:cpp:class:the_rule_of_zero_in_c_-_fluent_c_2019-12-18_23_16_30_.html |Archive du 23/04/2019 le 18/12/2019}} Autre explication : [[https://www.fluentcpp.com/2019/04/23/the-rule-of-zero-zero-constructor-zero-calorie/|The Rule of Zero in C++]] {{ :lang:cpp:class:the_rule_of_zero_in_c_-_fluent_c_2019-12-18_23_16_30_.html |Archive du 23/04/2019 le 18/12/2019}}
Ligne 89: Ligne 71:
 ===Destructeur=== ===Destructeur===
  
-Une classe doit avoir un destruteur virtuel si l'héritage est utilisée et que l'une des classes (elle, enfant ou parent) gère des ressources.+Une classe doit avoir un destructeur virtuel si l'héritage est utilisé et que l'une des classes (elle, enfant ou parent) gère des ressources. Cela inclut aussi des membres de type classe qui gère des ressources (par exemple ''std::string''), soit presque toutes les classes.
  
 L'attribut ''virtual'' doit être placé dans la classe la plus parent. L'attribut ''virtual'' doit être placé dans la classe la plus parent.
Ligne 145: Ligne 127:
 <code> <code>
 const const
 +</code>
 +
 +  * Durée de vie d'un objet dans la pile
 +
 +Si c'est un objet explicitement déclaré, le destructeur sera appelé lors du bloc fermant.
 +
 +<code cpp>
 +int main()
 +{
 +  A a;
 +  {
 +    B b;
 +    // On appelle ~B()
 +  }
 +  // On appelle ~A()
 +}
 +</code>
 +
 +La technique de mettre un bloc de crochet pour forcer un appel du destructeur à un moment précis est utilisé fréquemment par les ''std::scoped_lock''.
 +
 +<code cpp>
 +int main()
 +{
 +  std::mutex io_mutex;
 +  {
 +    std::scoped_lock<std::mutex> lk(io_mutex);
 +    std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
 +    // le mutex est automatiquement libéré lors de l'appel du destructeur de lk.
 +  }
 +  return 1;
 +}
 +</code>
 +
 +Si l'objet est temporaire, le destructeur sera appelé lorsque la totalité de l'instruction sera finie.
 +
 +<code cpp>
 +#include <iostream>
 +
 +class A
 +{
 +public:
 +  A() noexcept { std::cout << "A\n"; }
 +  ~A() noexcept { std::cout << "~A\n"; }
 +  int foo() { return 1; }
 +};
 +
 +int foo2(int a)
 +{
 +  std::cout << "int\n";
 +  return a;
 +}
 +
 +int main()
 +{
 +  std::cout << "0\n";
 +  foo2(foo2(A().foo()));
 +  // Le destructeur de A est appelé ici.
 +  std::cout << "1\n";
 +}
 +</code>
 +
 +Affichage :
 +
 +<code>
 +0
 +A
 +int
 +int
 +~A
 +1
 </code> </code>
  
Ligne 222: Ligne 274:
  
 L'opérateur ''copy'' devrait toujours être ''delete'' si la classe est polymorphique pour éviter les problèmes de slicing. [[https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual|C.67: A polymorphic class should suppress copying]] L'opérateur ''copy'' devrait toujours être ''delete'' si la classe est polymorphique pour éviter les problèmes de slicing. [[https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual|C.67: A polymorphic class should suppress copying]]
 +
 +Mauvais :
  
 <code cpp> <code cpp>
Ligne 240: Ligne 294:
 D d; D d;
 f(d); f(d);
 +</code>
 +
 +OK :
 +
 +<code cpp>
 +#include <memory>
 +
 +class B {
 + public:
 +  B() = default;
 +  // Si on implémente ce clone, il faut mettre le constructeur par copie en public.
 +  // Il est préférable de mettre ce constructeur en privée pour empêcher
 +  // sa mauvaise utilisation.
 +  virtual std::unique_ptr<B> clone() = 0;
 +  virtual char m() { return 1; }
 +  virtual ~B() = default;
 +
 + protected:
 +  // Peut être protected si clone est pure virtuelle.
 +  B(const B&) = default;
 +  B& operator=(const B&) = delete;
 +};
 +
 +class D : public B {
 + public:
 +  D() = default;
 +  D(const D&) = default;
 +  D& operator=(const D&) = delete;
 +  std::unique_ptr<B> clone() override { return std::make_unique<D>(*this); }
 +  char m() override { return 10; }
 +  virtual ~D() = default;
 +};
 +
 +char f(B& b) {
 +  auto b2 = b.clone();
 +  return b2->m();
 +}
 +
 +int main() {
 +  D d;
 +  return f(d);
 +}
 </code> </code>
  
Ligne 404: Ligne 500:
 } }
 </code> </code>
 +
 +====vtable====
 +
 +Il n'existe pas de norme pour savoir où est l'adresse du pointeur de la table virtuelle. Cependant, en tout logique, c'est la première donnée de la classe.
 +
 +<code cpp>
 +#include <iostream>
 +
 +struct S {
 +  int x;
 +  virtual void f() {}
 +};
 +
 +int main() {
 +  S s;
 +  s.x = 5;
 +  std::cout << "size : " << sizeof(S) << "\n";
 +  void*** ptr = (void***)&s;
 +  std::cout << "address : " << ptr << "\n";
 +  std::cout << "bytes : " << *ptr << " and " << *(ptr + 1) << "\n";
 +  std::cout << "address if vftable : " << *ptr << "\naddress of f : " << **ptr
 +            << "\n";
 +  std::cin.get();
 +  return 0;
 +}
 +</code>
 +
 +[[http://www.cplusplus.com/forum/general/33019/|detemine vtable address]] {{ :lang:cpp:class:detemine_vtable_address_-_c_forum_2021-06-21_00_03_46_.html |Archive du 13/12/2010 le 22/06/2021}}
 +
 ====override / redéfinition==== ====override / redéfinition====
 ===Cas général=== ===Cas général===
Ligne 439: Ligne 564:
  
 Lors de l'instantiation d'une classe, un constructeur sera systématiquement appelé à chaque niveau d'héritage, en commençant par celui en bas de la hiérarchie. Lors de l'instantiation d'une classe, un constructeur sera systématiquement appelé à chaque niveau d'héritage, en commençant par celui en bas de la hiérarchie.
 +
 +Il est possible d'appeler un constructeur depuis un autre constructeur. C'est pratique si on souhaite avoir deux constructeurs très proche.
 +
 +<code cpp>
 +class A{};
 +class B : public A
 +{
 + public:
 +  // Call constructor B(int) from constructor B().
 +  // No need (forbidden) to call A.
 +  B() : B(5){}
 +  // Avoid default function argument
 +  B(int a/* = 5*/):A(), a_(a){}
 +
 + private:
 +  int a_;
 +};
 +</code>
  
   * Destructeur   * Destructeur
lang/cpp/classes.1589840423.txt.gz · Dernière modification : de root