Vous faites presque certainement référence à des champs de type comme discuté dans le livre The C++ Programming Language par Bjarne Stroustrup. Un champ de type dans ce contexte serait simplement une variable de quelque sorte dans une classe de base qui indique le type réel de ses sous-classes. Voici un exemple:
class Pet
{
public:
enum PetType { Dog, Cat, Bird, Fish };
void ToString()
{
switch(type)
{
case Pet::Dog: std::cout << "Dog" << std::endl; break;
case Pet::Cat: std::cout << "Cat" << std::endl; break;
case Pet::Bird: std::cout << "Bird" << std::endl; break;
case Pet::Fish: std::cout << "Fish" << std::endl; break;
}
}
private:
PetType type; // A type field.
};
class Dog : public Pet
{
public:
Dog() { type = Dog; }
};
// And so on...
void Test(const Pet& p) { p.ToString(); }
int main()
{
Dog d;
Test(d);
return 0;
}
C'est une façon extraordinairement fragile pour mettre en œuvre une méthode ToString()
. Chaque fois que vous devez ajouter une classe dérivée de Pet
, vous devez mettre à jour l'énumération PetType
et la méthode ToString()
. Par exemple, si je besoin d'une sous-classe Turtle
, je besoin de faire ces changements:
// ...
enum PetType { Dog, Cat, Bird, Fish, Tutle /* Added */};
void ToString(const Pet& p)
{
switch(p.type)
{
case Pet::Dog: std::cout << "Dog" << std::endl; break;
case Pet::Cat: std::cout << "Cat" << std::endl; break;
case Pet::Bird: std::cout << "Bird" << std::endl; break;
case Pet::Fish: std::cout << "Fish" << std::endl; break;
case Pet::Turtle: std::cout << "Turtle" << std::endl; break; // Added
}
}
// ...
class Turtle : public Pet
{
public:
Turtle() { type = Turtle; } // Added
};
Imaginez si la classe Pet
avait plus de fonctions que tout ToString()
; l'entretien devient un cauchemar. C'est beaucoup de code que l'on doit changer, mais l'important est que pour avoir une classe Turtle
, j'ai besoin de modifier la classe Pet
. Cela signifie que plus de tests, de révision de code, etc. sont nécessaires. C'est une violation claire de the open/closed principle. C'est pourquoi les champs de type sont extrêmement sujets aux erreurs.
Une façon significativement supérieure serait d'utiliser les fonctions virtual
:
class Pet
{
public:
virtual void ToString() = 0;
};
class Dog : public Pet
{
public:
virtual void ToString() { std::cout << "Dog" << std::endl; }
};
class Turtle : public Pet
{
public:
virtual void ToString() { std::cout << "Turtle" << std::endl; }
};
// And so on...
void Test(const Pet& p) { p.ToString(); }
int main()
{
Turtle t
// Will call Turtle::ToString(), even though
// Test() was only given a const Pet&
Test(t);
return 0;
}
Notez que le code ci-dessus ne nécessite aucun enum
supplémentaire s ou switch
déclarations. Appeler Pet::ToString()
appellera l'implémentation correcte de ToString()
pour Dog
s, Cat
s, etc. automatiquement, avec beaucoup moins de code. Je n'ai même pas besoin de changer la classe Pet
; Je peux simplement déposer une classe Turtle
si nécessaire, à condition que Pet
ait été définie. Pour une utilisation éventuellement légitime des champs de type, see this Stack Overflow question and the answers to that question
Bonjour, peu importe combien j'essaie de ne pas comprendre ce que vous voulez dire et ce que vous essayez de demander. Pouvez-vous s'il vous plaît essayer de réécrire la question d'une manière plus intelligible? – Lefteris
Ceci est une Question valide.Si vous ne comprenez pas ce que le PO essaie de demander, ne votez pas cela comme * Pas un vrai Q * ou * Non Constructif *, ce n'est pas l'un d'entre eux. –
@AIs Je n'ai pas baissé l'OP. J'ai juste demandé des éclaircissements à sa question parce que je ne comprenais pas à quoi il faisait allusion. de toute façon la réponse de Silo semble être ce que le PO demandait. Je ne savais pas qu'ils se réfèrent à cette pratique en tant que champs de type – Lefteris