Utiliser le code C++ dans le Concepteur de classes

Le Concepteur de classes affiche une aire de conception visuelle appelée diagramme de classes, qui fournit une représentation visuelle des éléments de code dans votre projet. Vous pouvez utiliser des diagrammes de classes pour concevoir et visualiser des classes et d'autres types dans un projet.

Le Concepteur de classes prend en charge les éléments de code C++ suivants :

  • Classe (ressemble à une forme de classe managée, mais peut avoir plusieurs relations d'héritage)

  • Classe anonyme (affiche le nom généré de l'Affichage de classes pour le type anonyme)

  • Classe de modèle

  • Structure

  • Énumération

  • Macro (montre l'affichage post-traité de la macro)

  • Typedef

Notes

Cela est différent du diagramme de classes UML, que vous pouvez créer dans un projet de modélisation. Pour plus d’informations, consultez Diagrammes de classes UML : indications.

Classes C++ dans le Concepteur de classes

Le Concepteur de classes prend en charge les classes C++ et visualise les classes C++ natives de la même façon que les formes de classe Visual Basic et C#, à la différence près que les classes C++ peuvent avoir plusieurs relations d’héritage. Vous pouvez développer la forme de classe pour afficher plus de champs et de méthodes dans la classe, ou la réduire pour économiser l’espace.

Notes

Le Concepteur de classes ne prend pas en charge les unions (un type spécial de classe dans laquelle la mémoire allouée est uniquement suffisante pour la plus grande donnée membre de l’union).

Héritage simple

Quand vous faites glisser plusieurs classes sur un diagramme de classes et que les classes ont une relation d’héritage de classe, une flèche les connecte. La flèche pointe dans la direction de la classe de base. Par exemple, quand les classes suivantes sont affichées dans un diagramme de classes, une flèche les connecte en pointant de B vers A :

class A {};
class B : A {};

Vous pouvez également faire glisser uniquement la classe B sur le diagramme de classes, cliquer avec le bouton droit sur la forme de classe pour B, puis cliquer sur Afficher les classes de base. Cela affiche sa classe de base : A.

Héritage multiple

Le Concepteur de classes prend en charge la visualisation de relations d’héritage de classes multiples. L’héritage multiple est utilisé quand une classe dérivée a des attributs de plusieurs classes de base. Voici un exemple d’héritage multiple :

class Bird {};
class Swimmer {};
class Penguin : public Bird, public Swimmer {};

Quand vous faites glisser plusieurs classes sur le diagramme de classes et que les classes ont une relation d’héritage de classes multiples, une flèche les connecte. La flèche pointe dans la direction des classes de base.

Pour afficher les classes de base correspondant à la classe adoptée, cliquez avec le bouton droit sur une forme de classe, puis cliquez sur Afficher les classes de base.

Notes

La commande Afficher les classes dérivées n’est pas prise en charge pour le code C++. Pour afficher des classes dérivées, accédez à Affichage de classes, développez le nœud de type, le sous-dossier Types dérivés, puis faites glisser ces types sur le diagramme de classes.

Pour plus d’informations sur l’héritage de classes multiples, consultez Héritage multiple et Plusieurs classes de base.

Classes abstraites

Le Concepteur de classes prend en charge les classes abstraites (également appelées « classes de base abstraites »). Il s’agit de classes que vous n’instanciez jamais, mais desquelles vous pouvez dériver d’autres classes. Dans le cadre de l’exemple "Héritage multiple" présenté précédemment dans ce document, vous pouvez instancier la classe Bird comme objets individuels, comme suit :

int main()
{
   Bird sparrow;
   Bird crow;
   Bird eagle;
}

Toutefois, dans certains cas, vous pouvez ne pas souhaiter instancier la classe Swimmer en tant qu’objets individuels. Vous pouvez vouloir uniquement en dériver d’autres types de classes d’animaux, comme Penguin, Whale et Fish. Dans ce cas, vous déclarez la classe Swimmer comme classe de base abstraite.

Pour déclarer une classe abstraite, vous pouvez utiliser le mot clé abstract. Les membres marqués comme abstraits, ou inclus dans une classe abstraite, sont virtuels et doivent être implémentés par des classes qui dérivent de la classe abstraite.

class Swimmer abstract
{
   virtual void swim();
   void dive();
};

Vous pouvez également déclarer une classe comme abstraite en incluant au moins une fonction virtuelle pure :

class Swimmer
{
   virtual void swim() = 0;
   void dive();
};

Quand vous affichez ces déclarations dans un diagramme de classes, le nom de classe Swimmer et sa fonction virtuelle pure swim s’affichent en italique dans une forme de classe abstraite et sont accompagnés de la notation Classe abstraite. Notez que la forme de type de classe abstraite est identique à celle d’une classe normale, sauf que sa bordure est une ligne pointillée.

Pour pouvoir être instanciée, une classe dérivée d’une classe de base abstraite doit substituer chaque fonction virtuelle pure dans la classe de base. Ainsi, si vous dérivez une classe Fish à partir de la classe Swimmer, par exemple, Fish doit substituer la méthode swim :

class Fish : public Swimmer
{
   void swim(int speed);
};

int main()
{
   Fish guppy;
}

Quand vous affichez ce code dans un diagramme de classes, le Concepteur de classes trace une ligne d’héritage entre Fish et Swimmer.

Classes anonymes

Le Concepteur de classes prend en charge les classes anonymes. Les types de classes anonymes sont des classes déclarées sans identificateur. Elles ne peuvent pas avoir de constructeur ou de destructeur, ne peuvent pas être passées comme arguments aux fonctions et ne peuvent pas être retournées en tant que valeurs de retour de fonctions. Vous pouvez utiliser une classe anonyme pour remplacer un nom de classe par un nom de typedef, comme dans l’exemple suivant :

typedef struct
{
    unsigned x;
    unsigned y;
} POINT;

Les structures peuvent également être anonymes. Le Concepteur de classes affiche des classes et des structures anonymes de la même façon qu’il affiche le type respectif. Bien que vous puissiez déclarer et afficher des classes et des structures anonymes, le Concepteur de classes n’utilise pas le nom de balise que vous spécifiez. Il utilise le nom généré par l’affichage de classes. La classe ou la structure apparaît dans l’affichage de classes et le Concepteur de classes sous la forme d’un élément appelé __unnamed.

Pour plus d’informations sur les classes anonymes, consultez Types de classe anonymes.

Classes de modèle

Le Concepteur de classes prend en charge la visualisation de classes de modèle. Les déclarations imbriquées sont prises en charge. Le tableau suivant présente des déclarations classiques.

Élément de code Vue Concepteur de classes
template <class T>

class A {};
A<T>

Classe de modèle
template <class T, class U>

class A {};
A<T, U>

Classe de modèle
template <class T, int i>

class A {};
A<T, i>

Classe de modèle
template <class T, template <class K> class U>

class A {};
A<T, U>

Classe de modèle

Le tableau suivant présente quelques exemples de spécialisation partielle.

Élément de code Vue Concepteur de classes
template<class T, class U>

class A {};
A<T, U>

Classe de modèle
template<class T>

class A<T, T> {};
A<T, T>

Classe de modèle
template <class T>

class A<T, int> {};
A<T, int>

Classe de modèle
template <class T1, class T2>

class A<T1*, T2*> {};
A<T1*, T2*>

Classe de modèle

Le tableau suivant présente quelques exemples d’héritage dans la spécialisation partielle.

Élément de code Vue Concepteur de classes
template <class T, class U>

class A {};

template <class TC>

class A<T, int> {};

class B : A<int, float>

{};

class C : A<int, int>

{};
A<T, U>

Classe de modèle

B

Classe

(pointe vers la classe A)

C

Classe

(pointe vers la classe A)

Le tableau suivant présente quelques exemples de fonctions de modèle de spécialisation partielle.

Élément de code Vue Concepteur de classes
class A

{

template <class T, class U>

void func(T a, U b);

template <class T>

void func(T a, int b);

};
A

func<T, U> (+ 1 surcharge)
template <class T1>

class A {

template <class T2>

class B {};

};

template<> template<>

class A<type>::B<type> {};
A<T1>

Classe de modèle

B<T2>

Classe de modèle

(B est contenu dans la classe A sous Types imbriqués)
template <class T>

class C {};

class A : C<int> {};
A

Classe

-> C<int>

C<T>

Classe de modèle

Le tableau suivant présente quelques exemples d’héritage de modèle.

Élément de code Vue Concepteur de classes
template <class T>

class C {};

template<>

class C<int> {

class B {};

}

class A : C<int>::B {};
A

Classe

->B

C<int>

Classe

(B est contenu dans la classe C sous Types imbriqués)

C<T>

Classe de modèle

Le tableau suivant présente des exemples de connexion de classes spécialisées canoniques.

Élément de code Vue Concepteur de classes
template <class T>

class C {};

template<>

class C<int> {};

class A : C<int> {};

class D : C<float> {};
A

Classe

->C<int>

C<int>

Classe

C<T>

Classe de modèle

D

Classe

->C<float>
class B {

template <class T>

T min (const T &a, const T &b);

};
B

min <T>

Énumérations C++ dans le Concepteur de classes

Le Concepteur de classes prend en charge les types enum C++ et enum class dont la portée est définie. Vous trouverez ci-dessous un exemple :

enum CardSuit {
   Diamonds = 1,
   Hearts = 2,
   Clubs = 3,
   Spades = 4
};

// or...
enum class CardSuit {
   Diamonds = 1,
   Hearts = 2,
   Clubs = 3,
   Spades = 4
};

Une forme d’énumération C++ dans un diagramme de classes se présente et fonctionne comme une forme de structure, hormis le fait que l’étiquette indique Enum ou Enum class, elle est rose plutôt que bleu, et elle comporte une bordure colorée sur les marges gauche et supérieure. Les formes d’énumération et les formes de structure ont des angles carrés.

Pour plus d’informations sur l’utilisation du type enum, consultez Énumérations.

Typedefs C++ dans le Concepteur de classes

Les instructions Typedef créent une ou plusieurs couches d’indirection entre un nom et son type sous-jacent. Le Concepteur de classes prend en charge les types typedef C++ qui sont déclarés avec le mot clé typedef, par exemple :

typedef class coord
{
   void P(x,y);
   unsigned x;
   unsigned y;
} COORD;

Vous pouvez ensuite utiliser ce type pour déclarer une instance :

COORD OriginPoint;

Formes de classe et de struct

Dans le Concepteur de classes, un typedef C++ a la forme du type spécifié dans le typedef. Si la source déclare typedef class, la forme a des angles arrondis et l’étiquette Class. Pour typedef struct, la forme a des angles carrés et l’étiquette Struct.

Les classes et les structures peuvent avoir des typedefs imbriqués déclarés à l’intérieur. Dans le Concepteur de classes, les formes de classe et de structure peuvent afficher des déclarations typedef imbriquées comme formes imbriquées.

Les formes typedef prennent en charge les commandes Afficher en tant qu’association et Afficher en tant qu’association de collection dans le menu contextuel (clic droit).

Exemple de typedef de classe

class B {};
typedef B MyB;

C++ class typedef in Class Designer

Exemple de typedef de struct

typedef struct mystructtag
{
    int   i;
    double f;
} mystruct;

C++ struct typedef in Class Designer

Typedefs sans nom

Bien que vous puissiez déclarer un typedef sans nom, le Concepteur de classes n’utilise pas le nom de la balise que vous spécifiez. Le Concepteur de classes utilise le nom généré par l’affichage de classes. Par exemple, la déclaration suivante est valide, mais elle apparaît dans Affichage de classes et le Concepteur de classes comme objet nommé __unnamed :

typedef class coord
{
   void P(x,y);
   unsigned x;
   unsigned y;
};

Notes

Le Concepteur de classes n’affiche pas les typedefs dont le type source est un pointeur de fonction.

Découvrez plus d’informations sur les limitations pour des éléments de code C++

  • Quand un projet C++ est chargé, le Concepteur de classes fonctionne en lecture seule. Vous pouvez modifier le diagramme de classes, mais vous ne pouvez pas réenregistrer les modifications du diagramme de classes dans le code source.

  • Le Concepteur de classes prend en charge uniquement la sémantique C++ native. Pour les projets C++ compilés dans le code managé, le Concepteur de classes visualise uniquement les éléments de code qui sont des types natifs. Vous pouvez donc ajouter un diagramme de classes à un projet, mais le Concepteur de classes ne vous autorise pas à visualiser les éléments dans lesquels la propriété IsManaged a la valeur true (autrement dit, les types valeur et types référence).

  • Pour les projets C++, le Concepteur de classes lit uniquement la définition du type. Par exemple, supposons que vous définissez un type dans un fichier d'en-tête (.h) et que vous définissez ses membres dans un fichier d'implémentation (.cpp). Si vous appelez « Afficher le diagramme de classes » sur le fichier d’implémentation (.cpp), le Concepteur de classes n’affiche rien. Autre exemple, si vous appelez « Afficher le diagramme de classes » sur un fichier .cpp qui utilise une instruction #include pour inclure d’autres fichiers, mais qui ne contient pas de définitions de classe réelles, le Concepteur de classes n’affiche toujours rien.

  • Les fichiers IDL (.idl) qui définissent des interfaces COM et des bibliothèques de types ne s'affichent pas dans les diagrammes, sauf s'ils sont compilés en code C++ natif.

  • Le Concepteur de classes ne prend pas en charge les fonctions et variables globales.

  • Le Concepteur de classes ne prend pas en charge les unions. Il s'agit d'un type spécial de classe dans laquelle la mémoire allouée est uniquement suffisante pour la plus grande donnée membre de l'union.

  • Le Concepteur de classes n’affiche pas les types de données de base comme int et char.

  • Le Concepteur de classes n’affiche pas les types qui sont définis en dehors du projet actuel si le projet n’a pas de références correctes à ces types.

  • Le Concepteur de classes peut afficher les types imbriqués, mais pas les relations entre un type imbriqué et d’autres types.

  • Le Concepteur de classes ne peut pas afficher les types void ou dérivés d’un type void.

Résoudre les problèmes de résolution de type et d’affichage

Emplacement des fichiers sources

Le Concepteur de classes n’effectue pas un suivi de l’emplacement des fichiers sources. Si vous modifiez votre structure de projet ou que vous déplacez des fichiers sources dans votre projet, le Concepteur de classes peut donc perdre la trace du type (surtout le type source d’un typedef, de classes de base ou de types d’associations). Vous pouvez recevoir une erreur telle que Le Concepteur de classes n’est pas en mesure d’afficher ce type. Dans ce cas, refaites glisser le code source modifié ou déplacé vers le diagramme de classes pour le réafficher.

Problèmes de mise à jour et de performances

Pour les projets C++, la modification dans le fichier source peut prendre 30 à 60 secondes avant d’apparaître dans le diagramme de classes. Ce délai peut également forcer le Concepteur de classes à générer l’erreur Aucun type n’a été trouvé dans la sélection. Si vous obtenez une telle erreur, cliquez sur Annuler dans le message d’erreur et attendez que l’élément de code apparaisse dans l’Affichage de classes. Le Concepteur de classes doit ensuite pouvoir afficher le type.

En cas d'échec de la mise à jour d'un diagramme de classes suite à la modification du code, il peut s'avérer nécessaire de fermer le diagramme, puis de le rouvrir.

Problèmes de résolution de type

Le Concepteur de classes peut ne pas être en mesure de résoudre des types pour les raisons suivantes :

  • Le type se trouve dans un projet ou un assembly non référencé à partir du projet qui contient le diagramme de classes. Pour corriger cette erreur, ajoutez une référence au projet ou à l'assembly qui contient le type. Pour plus d’informations, consultez Gestion des références dans un projet.

  • Le type ne se trouvant pas dans la portée correcte, le Concepteur de classes ne peut pas le localiser. Vérifiez qu'il ne manque pas une instruction using, imports ou #include au code. Assurez-vous également que vous n'avez pas déplacé le type (ou un type connexe) hors de l'espace de noms dans lequel il a été initialement localisé.

  • Le type n'existe pas (ou a été commenté). Pour corriger cette erreur, assurez-vous que vous n'avez pas commenté ni supprimé le type.

  • Le type se trouve dans une bibliothèque référencée par une directive #import. Une solution de contournement possible consiste à ajouter manuellement le code généré (le fichier .tlh) à une directive #include dans le fichier d'en-tête.

  • Vérifiez que le Concepteur de classes prend en charge le type que vous avez entré. Consultez Limitations pour les éléments de code C++.

L’erreur la plus probable pour un problème de résolution de type est la suivante : Code introuvable pour une ou plusieurs formes dans le diagramme de classes ’<élément>’. Ce message d'erreur n'indique pas nécessairement que votre code est erroné. Il indique seulement que le Concepteur de classes n'a pas pu afficher votre code. Essayez les actions suivantes :

  • Assurez-vous que le type existe. Vérifiez que vous n'avez pas involontairement commenté ni supprimé le code source.

  • Essayez de résoudre le type. Le type se trouve peut-être dans un projet ou un assembly non référencé à partir du projet qui contient le diagramme de classes. Pour corriger cette erreur, ajoutez une référence au projet ou à l'assembly qui contient le type. Pour plus d’informations, consultez Gestion des références dans un projet.

  • Assurez-vous que le type se trouve dans la portée correcte afin que le Concepteur de classes puisse le localiser. Vérifiez qu'il ne manque pas une instruction using, imports ou #include au code. Assurez-vous également que vous n'avez pas déplacé le type (ou un type connexe) hors de l'espace de noms dans lequel il a été initialement localisé.

Astuce

Pour obtenir plus d’informations sur la résolution des problèmes, consultez Erreurs du Concepteur de classes.