This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
cpp:syntax:class:generic [2021/04/06 14:47] bmonsuez created |
cpp:syntax:class:generic [2022/11/18 10:47] (current) |
||
---|---|---|---|
Line 6: | Line 6: | ||
<code cpp> | <code cpp> | ||
+ | #include<algorithm> | ||
+ | |||
class buffer | class buffer | ||
{ | { | ||
Line 24: | Line 26: | ||
} | } | ||
- | operator uint8_t*() const { return m_buffer; } | + | operator const uint8_t*() const { return m_buffer; } |
+ | operator uint8_t*() { return m_buffer; } | ||
+ | | ||
uint8_t operator[](int anIndex) const | uint8_t operator[](int anIndex) const | ||
{ | { | ||
Line 40: | Line 44: | ||
</code> | </code> | ||
- | Cette classe définit un buffer d'une taille de 128 octets. | + | Cette classe définit un buffer d'une taille de 128 octets. Elle redéfinit les opérateurs suivant : |
+ | * ''operator[]'' qui permet d'accéder en lecture et en écriture au buffer interne natif. | ||
+ | * ''operator uint8_t*()'' qui permet de récupérer un pointeur autorisant la lecture ou l'écriture dans le buffer vu. | ||
+ | * ''operator =(const buffer&)'' et ''buffer(const buffer&)'' correspondant à l'opérateur de recopie. | ||
+ | |||
+ | |||
+ | Nous voyons que si nous souhaitons faire un buffer d'une taille différente, nous devons définir une nouvelle classe dont la seule différence est la taille du buffer. | ||
+ | |||
+ | De même si nous souhaitons faire un buffer qui va stocker des éléments de base autres que des entiers 8 bits, par exemple des entiers 64 bits, il est nécessaire de réécrire une nouvelle classe en remplaçant ''uint8_t'' par un type différent. | ||
+ | |||
+ | ===== Paramétriser une classe par un type ===== | ||
+ | |||
+ | Dans un premier temps, nous allons paramétriser la classe ''buffer'' par le type des éléments qui sont contenus dans le ''buffer''. | ||
+ | |||
+ | Pour ce faire, nous allons déclarer la classe ''buffer'' comme étant un modéle de classe à l'instar des [[cpp:syntax:functions:template|modèles de fonctions]]. | ||
+ | |||
+ | <code cpp> | ||
+ | #include<algorithm> | ||
+ | |||
+ | template<class itemT> | ||
+ | class buffer | ||
+ | { | ||
+ | private: | ||
+ | itemT m_buffer[128]; | ||
+ | |||
+ | public: | ||
+ | |||
+ | buffer(const buffer<itemT>& anotherBuffer) | ||
+ | { | ||
+ | std::copy(anotherBuffer.m_buffer, anotherBuffer.m_buffer + 128, m_buffer); | ||
+ | } | ||
+ | int size() const { return 128; } | ||
+ | buffer& operator = (const buffer<itemT>& anotherBuffer) | ||
+ | { | ||
+ | std::copy(anotherBuffer.m_buffer, anotherBuffer.m_buffer + 128, m_buffer); | ||
+ | return this; | ||
+ | } | ||
+ | |||
+ | operator const itemT*() const { return m_buffer; } | ||
+ | operator itemT*() { return m_buffer; } | ||
+ | |||
+ | itemT operator[](int anIndex) const | ||
+ | { | ||
+ | if(anIndex >= 128) | ||
+ | throw std::out_of_range("index larger than buffer"); | ||
+ | return m_buffer[anIndex]; | ||
+ | } | ||
+ | itemT& operator[](int anIndex) | ||
+ | { | ||
+ | if(anIndex >= 128) | ||
+ | throw std::out_of_range("index larger than buffer"); | ||
+ | return m_buffer[anIndex]; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | La déclaration de la classe comme un modèle de classe s'effectue par : | ||
+ | |||
+ | <code cpp> | ||
+ | template<class itemT> | ||
+ | class buffer | ||
+ | </code> | ||
+ | |||
+ | qui indique que la classe est un __template__ qui a un type paramètre qui est le type des éléments stockés dans le buffer, soit le type ''itemT''. | ||
+ | |||
+ | Désormais, pour utiliser la classe ''buffer'', il est nécessaire de spécifier le type de ses éléments. Ainsi, quand je souhaite créer un buffer de caractères, il suffit d'écrire ''buffer<char>'': | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | void funct() | ||
+ | { | ||
+ | buffer<char> bufferOfCharacters; | ||
+ | bufferOfCharacters[0] = 't'; | ||
+ | bufferOfCharacters[1] = 'e'; | ||
+ | bufferOfCharacters[2] = 's'; | ||
+ | bufferOfCharacters[3] = 't'; | ||
+ | std::string token((const char*)bufferOfCharacters, (const char*)bufferOfCharacters + 4); | ||
+ | std::cout << token << std::endl; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Cette fonction crée un buffer de 128 caractères dénommé ''bufferOfCharacters'', stocke dans le buffer les caractères 't', 'e', 's', 't', copie ces 4 caractères dans un objet ''string'' et affiche le contenu de l'objet ''string'' sur la console. | ||
+ | |||
+ | Il est possible de paramétrer des classes avec plus d'un type. Par exemple, la déclaration de la classe [[https://en.cppreference.com/w/cpp/string/basic_string|std::basic_string]] de la STL définit trois paramètres de type ''charT'', ''traits'' et ''allocator'' comme suit : | ||
+ | |||
+ | <code cpp> | ||
+ | template< | ||
+ | class charT, | ||
+ | class traits = std::char_traits<charT>, | ||
+ | class allocator = std::allocator<charT> | ||
+ | class basic_string; | ||
+ | </code> | ||
+ | |||
+ | Il est intéressant de constater qu'il est possible de définir un paramètre de type par défaut. Ainsi, écrire : | ||
+ | |||
+ | <code cpp> | ||
+ | std::basic_string<char> myString; | ||
+ | </code> | ||
+ | |||
+ | est équivalent à écrire : | ||
+ | |||
+ | <code cpp> | ||
+ | std::basic_string<char, std::char_traits, std::allocator<charT>> myString; | ||
+ | </code> | ||
+ | |||
+ | Si nous ne spécifions pas les types ''traits'' et ''allocator'', le compilateur utilise les types par défaut. | ||
+ | |||
+ | |||
+ | ===== Paramétriser une classe par une valeur ===== | ||
+ | |||
+ | Nous souhaitons pouvoir choisir la taille du buffer. Pour ce faire, nous allons définir un modèle de classe qui est paramétrisé par la taille que nous souhaitons allouer au buffer. | ||
+ | |||
+ | |||
+ | <code cpp> | ||
+ | #include<algorithm> | ||
+ | |||
+ | template<class itemT, int bufferSize = 128> | ||
+ | class buffer | ||
+ | { | ||
+ | private: | ||
+ | itemT m_buffer[bufferSize]; | ||
+ | |||
+ | public: | ||
+ | |||
+ | buffer(const buffer<itemT, bufferSize>& anotherBuffer) | ||
+ | { | ||
+ | std::copy(anotherBuffer.m_buffer, anotherBuffer.m_buffer + bufferSize, m_buffer); | ||
+ | } | ||
+ | int size() const { return bufferSize; } | ||
+ | buffer& operator = (const buffer<itemT, bufferSize>& anotherBuffer) | ||
+ | { | ||
+ | std::copy(anotherBuffer.m_buffer, anotherBuffer.m_buffer + bufferSize, m_buffer); | ||
+ | return this; | ||
+ | } | ||
+ | |||
+ | operator const itemT*() const { return m_buffer; } | ||
+ | operator itemT*() { return m_buffer; } | ||
+ | |||
+ | itemT operator[](int anIndex) const | ||
+ | { | ||
+ | if(anIndex >= bufferSize) | ||
+ | throw std::out_of_range("index larger than buffer"); | ||
+ | return m_buffer[anIndex]; | ||
+ | } | ||
+ | itemT& operator[](int anIndex) | ||
+ | { | ||
+ | if(anIndex >= bufferSize) | ||
+ | throw std::out_of_range("index larger than buffer"); | ||
+ | return m_buffer[anIndex]; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Désormais, quand nous créons un buffer, nous pouvons spécifier la taille du buffer. | ||
+ | |||
+ | <code cpp> | ||
+ | buffer<int, 64> bufferOfIntegers; // buffer contenant 64 entiers. | ||
+ | buffer<char, 32> bufferOfChars; // buffer contenant 32 caractères. | ||
+ | buffer<float> bufferOfFloats; // buffer contenant 128 (la valeur par défaut) nombres à virgule flottante. | ||
+ | </code> | ||
+ | ===== Pour aller plus loin ===== | ||
+ | [[https://en.cppreference.com/w/cpp/language/class_template|Template class]] | ||
+ | [[https://en.cppreference.com/w/cpp/language/template_parameters|Template parameters and template arguments]] | ||
+ | [[https://en.cppreference.com/w/cpp/language/template_argument_deduction|Template argument deduction]] | ||
+ | [[https://en.cppreference.com/w/cpp/language/constraints|Contraints & Concepts]] | ||