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/13 11:31] – Fin de la définition de la règle des 5 / 0 rootlang:cpp:classes [2022/07/04 10:05] (Version actuelle) – [Destructeur] : lock_guard->scoped_lock root
Ligne 3: Ligne 3:
 ====Multiples constructeurs==== ====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. Il est possible d'appeler un constructeur depuis un autre constructeur. C'est pratique si on souhaite avoir deux constructeurs très proche.
 +
 +Bien qu'on soit dans le paragraphe sans héritage, j'ai mis un cas avec héritage pour montrer qu'on a pas besoin d'appeler le constructeur du parent si on appelle un autre constructeur.
  
 <code cpp> <code cpp>
Ligne 10: Ligne 12:
  public:  public:
   // Call constructor B(int) from constructor B().   // Call constructor B(int) from constructor B().
-  // Dont call the parent A() when calling another constructor of the same class.+  // No need (forbiddento call A.
   B() : B(5){}   B() : B(5){}
   // Avoid default function argument   // Avoid default function argument
Ligne 145: Ligne 147:
 <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 294:
  
 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 314:
 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 520:
 } }
 </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 446: Ligne 591:
 Pour le destructeur, toujours s'il est ''virtual'', tous les destructeurs seront appelés, de l'enfant vers le parent. Pour le destructeur, toujours s'il est ''virtual'', tous les destructeurs seront appelés, de l'enfant vers le parent.
  
 +Par défaut, un destructeur est ''noexcept''. Il est donc interdit d'y générer une exception. Il est possible d'ajouter ''noexcept(false)'' mais c'est une mauvaise pratique de codage.
 ====overload / surcharge==== ====overload / surcharge====
   * Cas général   * Cas général
Ligne 529: Ligne 675:
 </code> </code>
  
 +====Interface====
 +
 +Il n'y a pas une unique méthode pour définir une interface.
 +
 +===Classe abstraite===
 +
 +Il faut faire une classe avec tous les prototypes et les déclarer toutes virtuelles pures.
 +
 +''Foo'' et ''Bar'' doivent déclarer au minimum les méthodes de ''BaseClass''.
 +
 +<code cpp>
 +#include <iostream>
 +#include <memory>
 +#include <string>
 +#include <vector>
 +
 +struct BaseClass
 +{
 +  virtual ~BaseClass() = default;
 +  virtual std::string getName() const = 0;
 +};
 +
 +struct Bar : BaseClass
 +{
 +  std::string getName() const override
 +  {
 +    return "Bar";
 +  }
 +};
 +
 +struct Foo : BaseClass
 +{
 +  std::string getName() const override
 +  {
 +    return "Foo";
 +  }
 +};
 +
 +int main()
 +{
 +  std::vector<std::unique_ptr<BaseClass>> vec;
 +  vec.emplace_back(std::make_unique<Foo>());
 +  vec.emplace_back(std::make_unique<Bar>());
 +
 +  for (const auto& v : vec) std::cout << v->getName() << std::endl;
 +
 +  std::cout << std::endl;
 +}
 +</code>
 +
 +===Template===
 +
 +''Foo'' et ''Bar'' n'héritent pas explicitement d'une classe commune. Ce mécanisme est caché dans un objet opaque (type-erasure).
 +
 +<WRAP center round important 60%>
 +Ce système de masquage ne doit être appliqué que si nécessaire.
 +
 +[[https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rt-erasure|Where possible, avoid type-erasure]]
 +</WRAP>
 +
 +
 +Ici, ''Foo'', ''Bar'' et aussi ''Object'' et ''Model'' doivent déclarer au minimum les méthodes de ''BaseClass''.
 +
 +<code cpp>
 +#include <iostream>
 +#include <memory>
 +#include <string>
 +#include <vector>
 +
 +class Object
 +{
 + public:
 +  template <typename T>
 +  Object(std::unique_ptr<T> obj) : object(std::make_unique<Model<T>>(std::move(obj))) {}
 +
 +  std::string getName() const
 +  {
 +    return object->getName();
 +  }
 +
 +  struct Concept
 +  {
 +    virtual ~Concept() {}
 +    virtual std::string getName() const = 0;
 +  };
 +
 +  template< typename T >
 +  struct Model final : Concept
 +  {
 +    Model(std::unique_ptr<T> t) noexcept : object(std::move(t)) {}
 +    std::string getName() const final
 +    {
 +      return object->getName();
 +    }
 +   private:
 +    std::unique_ptr<T> object;
 +  };
 +
 +  std::unique_ptr<const Concept> object;
 +};
 +
 +struct Bar
 +{
 +  std::string getName() const
 +  {
 +    return "Bar";
 +  }
 +};
 +
 +struct Foo
 +{
 +  std::string getName() const
 +  {
 +    return "Foo";
 +  }
 +};
 +
 +int main()
 +{
 +  std::vector<std::unique_ptr<Object>> vec;
 +  vec.emplace_back(std::make_unique<Object>(std::make_unique<Foo>()));
 +  vec.emplace_back(std::make_unique<Object>(std::make_unique<Bar>()));
 +
 +  for (const auto& v : vec)
 +    std::cout << v->getName() << std::endl;
 +}
 +</code>
 +
 +[[https://www.modernescpp.com/index.php/c-core-guidelines-type-erasure-with-templates|C++ Core Guidelines: Type Erasure with Templates]] {{ :lang:cpp:class:c_core_guidelines_type_erasure_with_templates_-_modernescpp.com_2020-05-17_3_50_54_pm_.html |Archive du 17/09/2018 le 17/05/2020}}
 =====Hack===== =====Hack=====
  
lang/cpp/classes.1589362271.txt.gz · Dernière modification : 2020/05/13 11:31 de root