J'apprends le C++, mais je suis confus au sujet de la classe abstraite et de la classe concrète. Quelques exemples réels seraient appréciés.
Une classe abstraite est une classe pour laquelle une ou plusieurs méthodes sont déclarées mais non définies, ce qui signifie que le compilateur sait que ces méthodes font partie de la classe, mais pas quel code exécuter pour cette méthode. Ces méthodes sont appelées méthodes abstraites. Voici un exemple de classe abstraite.
class shape {
public:
virtual void draw() = 0;
};
Cela déclare une classe abstraite qui spécifie que tous les descendants de la classe doivent implémenter la méthode draw si la classe doit être concrète. Vous ne pouvez pas instancier cette classe car elle est abstraite, après tout, le compilateur ne saurait pas quel code exécuter si vous appelez member draw. Vous ne pouvez donc pas effectuer les opérations suivantes:
shape my_shape();
my_shape.draw();
Pour pouvoir réellement utiliser la méthode draw, vous devez dériver des classes de cette classe abstraite, qui implémentent la méthode draw, ce qui rend les classes concrètes:
class circle : public shape {
public:
circle(int x, int y, int radius) {
/* set up the circle */
}
virtual draw() {
/* do stuff to draw the circle */
}
};
class rectangle : public shape {
public:
rectangle(int min_x, int min_y, int max_x, int max_y) {
/* set up rectangle */
}
virtual draw() {
/* do stuff to draw the rectangle */
}
};
Vous pouvez maintenant instancier le cercle et le rectangle des objets concrets et utiliser leurs méthodes de dessin:
circle my_circle(40, 30, 10);
rectangle my_rectangle(20, 10, 50, 15);
my_circle.draw();
my_rectangle.draw();
Maintenant, bien sûr, la question est, pourquoi voudriez-vous faire cela? Ne pourriez-vous pas tout aussi bien avoir défini les classes de cercle et de rectangle et avoir supprimé toute la classe de forme? Vous pourriez, mais alors vous ne pourriez pas profiter de leur héritage:
std::vector<shape*> my_scene;
my_scene.Push_back(new circle(40, 30, 10));
my_scene.Push_back(new rectangle(20, 10, 50, 15));
std::for_each(my_scene.begin(), my_scene.end(), std::mem_fun_ref(&shape::draw)
Ce code vous permet de rassembler toutes vos formes dans un seul conteneur. Cela le rend beaucoup plus facile si vous avez beaucoup de formes et de nombreuses formes différentes dans votre scène. Par exemple, nous pouvons maintenant dessiner toutes les formes en une seule fois, et le code qui le fait n'a même pas besoin de connaître les différents types de formes que nous avons.
Maintenant, enfin, nous devons savoir pourquoi la fonction de dessin de la forme est abstraite, et pas seulement une fonction vide, c'est-à-dire pourquoi n'avons-nous pas simplement défini:
class shape {
public:
virtual void draw() {
/* do nothing */
}
};
La raison en est que nous ne voulons pas vraiment d'objets de forme type, ils ne seraient de toute façon pas de vraies choses, ils seraient abstraits. Il n'est donc pas logique de définir une implémentation pour la méthode draw, même vide. Rendre la classe de forme abstraite nous empêche d'instancier par erreur la classe de forme ou d'appeler par erreur la fonction de dessin vide de la classe de base au lieu de la fonction de dessin des classes dérivées. En effet, nous définissons une interface pour toute classe qui voudrait se comporter comme une forme, nous disons que toute classe de ce type devrait avoir une méthode de dessin qui ressemble à ce que nous avons spécifié.
Pour répondre à votre dernière question, il n'existe pas de "classe dérivée normale", chaque classe est soit abstraite, soit concrète. Une classe qui a des méthodes abstraites est abstraite, toute classe qui n'en a pas est concrète. C'est juste un moyen de différencier les deux types de classes. Une classe de base peut être abstraite ou concrète et une classe dérivée peut être abstraite ou concrète:
class abstract_base {
public:
virtual void abstract_method1() = 0;
virtual void abstract_method2() = 0;
};
class concrete_base {
public:
void concrete_method1() {
/* do something */
}
};
class abstract_derived1 : public abstract_base {
public:
virtual void abstract_method3() = 0;
};
class abstract_derived2 : public concrete_base {
public:
virtual void abstract_method3() = 0;
};
class abstract_derived3 : public abstract_base {
public:
virtual abstract_method1() {
/* do something */
}
/* note that we do not provide an implementation for
abstract_method2 so the class is still abstract */
};
class concrete_derived1 : public concrete_base {
public:
void concrete_method2() {
/* do something */
}
};
class concrete_derived2 : public abstract_base {
public:
virtual void abstract_method1() {
/* do something */
}
virtual void abstract_method2() {
/* do something */
}
/* This class is now concrete because no abstract methods remain */
};
classe abstraite ne peut pas être utilisé pour créer un objet. Alors que classe concrète peut être utilisé pour créer un objet.
Concret signifie '' existant dans la réalité ou dans l'expérience réelle; perceptible par les sens; réal''. Alors que, abstrait signifie 'non appliqué ou pratique; théorique'.
Une classe abstraite ne peut pas être instanciée. Alors que, un béton on peut.
Une classe abstraite est une classe qui a une ou plusieurs fonctions virtuelles pures. Alors qu'une classe concrète n'a pas de fonctions virtuelles pures.
Une classe concrète est une classe qui peut être utilisée pour créer un objet. Une classe abstraite ne peut pas être utilisée pour créer un objet (vous devez étendre une classe abstraite et créer une classe concrète pour pouvoir ensuite créer un objet).
Imaginez qu'il existe une machine qui peut "tamponner" les matières premières et fabriquer une voiture. La matrice est une classe concrète. À partir de cela, nous pouvons créer des objets de voiture. Une classe abstraite serait les plans pour la matrice. Vous ne pouvez pas faire de voitures à partir des plans du stamper, vous devez d'abord faire la classe du stamper à partir des plans.
Une classe abstraite ne peut pas être instanciée alors qu'une classe concrète le peut. Une classe abstraite sert de "plan" pour les classes dérivées, celles qui peuvent être instanciées.
Par exemple. Car
classe (abstraite) tandis que Audi S4
classe (dérivant de Car
) classe est une implémentation concrète.
Une grande partie de cela est abordée dans ces autres questions:
La classe de base contre la classe dérivée est un concept orthogonal à la classe abstraite contre la classe concrète.
Une classe de base est une classe qui n'hérite d'aucune autre classe. Une classe dérivée hérite d'une autre classe.
Une classe abstraite est une classe qui a une ou plusieurs fonctions virtuelles pures. Une classe concrète n'a pas de virtuels purs.
Une classe abstraite peut être soit une classe de base soit une classe dérivée (elle est dérivée d'une autre classe abstraite). Une classe concrète peut également être de base ou dérivée. Vous pouvez même dériver une classe abstraite d'une classe concrète, en ajoutant une fonction virtuelle pure à la classe dérivée. Mais en général, il existe une classe abstraite de base et une ou plusieurs classes dérivées concrètes.
Un bon exemple d'utilisation d'une classe abstraite est lorsque vous construisez quelque chose de très modulaire. Supposons que vous travaillez avec un magasin de données, mais ces données peuvent être dans une base de données MySQL, une base de données SQLite, un fichier XML ou du texte brut. Pour conserver cette polyvalence dans votre code, vous pouvez créer une classe AbstractDatastore
qui définit les méthodes publiques que vous souhaitez utiliser pour obtenir des informations du magasin de données. Ensuite, vous créez vos implémentations spécifiques de AbstractDatastore
, telles que XmlDatastore
, SQLiteDatastore
, etc. Ensuite, votre programme a juste besoin de savoir qu'il obtient un AbstractDatastore
et qu'il doit avoir ces fonctions définies dans AbstractDatastore
mais il ne sait pas ou ne se soucie pas comment les données sont stockées ou récupérées.
C++ Faq Lite est un excellent site pour rechercher des réponses à ce type de questions.
Au niveau de la conception, une classe de base abstraite (ABC) correspond à un concept abstrait. Si vous demandiez à un mécanicien s'il réparait des véhicules, il se demanderait probablement à quel type de véhicule vous pensiez. Il y a de fortes chances qu'il ne répare pas les navettes spatiales, les paquebots, les vélos ou les sous-marins nucléaires. Le problème est que le terme "véhicule" est un concept abstrait (par exemple, vous ne pouvez pas construire un "véhicule" à moins de savoir quel type de véhicule construire). En C++, la classe Vehicle serait un ABC, avec Bicycle, SpaceShuttle, etc., étant des classes dérivées (un OceanLiner est une sorte de véhicule). Dans le monde réel OO, les ABC apparaissent partout
Une classe abstraite est une classe qui a une ou plusieurs fonctions de membre virtuel pur. Vous ne pouvez pas créer un objet (instance) d'une classe abstraite
class Shape {
public:
virtual void draw() const = 0; // = 0 means it is "pure virtual"
...
};
La classe concrète a toute sa méthode implémentée. Abstrait classe toute sa méthode sauf une (au moins une) méthode (s) non implémentée (s) afin que vous puissiez l'étendre et implémenter la méthode non implémentée.
Avantage: en étendant à partir de la classe abstraite, vous obtenez toutes les fonctionnalités de la classe de base et vous serez "obligé" d'implémenter la méthode non implémentée. Le concepteur de la classe vous oblige donc essentiellement à écrire du code dans la méthode abstraite avant que la classe ne vous soit d'aucune utilité.