The K Desktop Environment

Page suivante Page précédente Table des matières

15. Conseils de Programmation

Approchant de la fin de ce manuel, j'aimerais résumer plusieurs points auxquels les programmeurs devraient être vigilants pendant le codage. Ceux-ci sont principalement des astuces de programmation C++ ayant trait à la programmation KDE et Qt en particulier et sont partiellement issus du Centre du Développeur KDE que vous pouvez trouver sur Internet à l'adresse http://www.kde.org, d'autres sont le fruit de mon expérience personnelle.

15.1 Règles générales

Noms de fichiers

Tout d'abord, lorsque vous créez des fichiers sources, vous devriez toujours utiliser des noms de fichiers en minuscules. KDevelop supporte ceci si vous utilisez l'auto-suggestion de noms de fichiers. Cela permet aux autres développeurs de se souvenir plus facilement des fichiers sources à rechercher lorsqu'ils doivent déboguer votre application.

Noms de classes

Il est conseillé que les noms de classes des projets KDE respectent ces règles :

  • les noms de classe devraient commencer par la lettre K suivie du nom de la classe exprimant son intérêt. Cela peut être par exemple KMyWidget pour un widget spécifique à une application ;
  • les champs de classe devraient toujours commencer par des minuscules, avec la première lettre en majuscule pour les mots suivants, comme par exemple myWidgetPointer() ;
  • les méthodes qui renvoient la valeur d'un champ privé ne devraient pas utiliser le préfixe get. Vous devriez lui préférer un nom descriptif. Exemple : b_myboolean est un champ privé. La méthode renvoyant la valeur actuelle serait par exemple myBoolean().

Accès aux fichiers au sein du code

Vous devez éviter de coder tout chemin en dur, et utiliser les Standards du Système de Fichiers de KDE. Vous avez juste à vérifier le chemin d'installation de vos fichiers par les macros de Makefile.am comme décrit dans le présent manuel. Au sein du code, vous devriez utiliser les méthodes de KApplication pour extraire le chemin réel.

Documentation des classes

Autre point déjà mentionné, la documentation des classes. Vous devriez utiliser les règles de formatage de KDoc telles qu'elles sont utilisées par tous les développeurs KDE pour documenter leurs classes. Vous devriez ajouter au moins une ligne à chacun des membres de vos classes pour que vous puissiez vous souvenir de leur but et pour que les autres puissent réutiliser votre code. La réutilisation du code par la GPL est bien plus sensée si vous savez où trouver une solution existante et si les classes sont documentées. La Référence de la Bibliothèque Qt est un bon exemple d'interfaces bien documentées, bien qu'elle n'utilise pas KDoc.

Utilisez new pour créer des widgets

Au sein de votre implantation, vous devriez toujours préférer la création massive de widgets avec new. La bibliothèque Qt a la bonne habitude de supprimer automatiquement tous les widgets fils créés avec new, vous n'avez donc pas à utiliser delete dans ces cas. C'est une des fonctionnalités pratiques des plus importantes de Qt et vous devriez en user et en abuser.

Deboguage

Quand vient le moment de déboguer, vous devriez utiliser les macros fournies par KDebug. Celles-ci sont similaires aux macros Qt, mais peuvent être obtenues par CTRL+SHIFT+F12. Voir Référence des Bibliothèques KDE pour plus d'informations sur le filtrage d'évenements de ces macros. Vous pourriez aussi utiliser assert(), mais vous devriez essayer d'être logique dans votre code de déboguage.

Déclarations const

Par la suite, vous devriez utiliser des déclarations const pour les méthodes qui ne devraient changer aucun champ privé. Ce serait le cas pour toutes les méthodes qui retournent seulement la valeur actuelle d'un champ privé. Cela permet d'éviter de modifier une valeur accidentellement et de détecter de telles erreurs dès la compilation. Maintenant, pour l'initialisation des membres const vous devriez combiner const avec static et initialiser la valeur en dehors du constructeur, comme ceci :

class foo {
        static const int value;
};

const foo::value = 10;
Le C++ ANSI autorise l'initialisation du champ dans le constructeur mais vous devriez l'éviter car certains compilateurs n'offrent pas cette fonctionnalité.

Les méthodes virtuelles

Comme nous l'avons vu dans la section Interaction avec l'Utilisateur, vous devriez conserver les droits d'accès et la déclaration par virtual lorsque vous surchargez des méthodes virtuelles. Tout au moins, vous ne devriez pas réduire l'accès d'une méthode virtuelle de protégé à privé.

Déclarations anticipées

Les en-têtes de classe doivent être inclus lorsque vous déréférencez n'importe quel objet ou instance de classe dans votre code source. Cela signifie que si votre classe utilise un membre d'une autre classe, vous devriez remplacer la directive #include par une déclaration anticipée de la classe. Par exemple, au lieu de :

#include <qpushbutton.h>

class KMyWidget:public QWidget
{

private:
  QPushButton* ok_button;
};

déclarez seulement la classe QPushButton dans le fichier d'en-tête :

class QPushButton;

class KMyWidget:public QWidget
{

private:
  QPushButton* ok_button;
};

et placez la directive d'inclusion dans le fichier source correspondant où, par exemple, l'instance ok_button est déréférencée avec une méthode de la classe QPushButton. Cela permet de gagner du temps à la compilation, spécialement si vous utilisez des instances de classes sur lesquelles vous êtes en train de travailler. Le compilateur recompilera tous les sources qui incluent le fichier d'en-tête si vous avez effectué des modifications à l'interface de la classe, c'est pourquoi un simple ajout d'une méthode qui retourne seulement une valeur interne provoquera le recompilation de tous les sources qui incluent le fichier d'en-tête de cette classe.

Avertissements de Paramètres Inutiles et arguments par défaut

Vous devriez aussi omettre les paramètres formels des méthodes qui ne nécessitent pas forcément ce paramètre pour fonctionner. Cela évite les avertissements de paramètre inutilisé de votre compilateur quand il voit une méthode qui attend un paramètre formel mais ne l'utilise pas dans son implantation. Généralement, vous définirez des arguments par défaut pour plusieurs méthodes. Ils devraient toujours être placés dans la déclaration du membre de la classe au lieu de les définir dans l'implantation des méthodes.

Utiliser config.h

Les projets KDevelop comme tout autre projet qui utilise autoconf pour créer les script configure produisent un fichier config.h après l'exécution du script configure sur la machine cible. Les valeurs trouvées par configure sont listées dedans et peuvent être utilisées dans le code source. La directive pour inclure le fichier config.h est :

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Une des entrées de config.h les plus couramment utilisées est probablement le définition du type bool pour les compilateurs qui ne respectent pas la norme la plus récente du C++ ANSI.

Utiliser 0 au lieu de NULL

Vous devriez toujours utiliser directement 0 au lieu de NULL pour les valeurs prédéfinies comme le font les bibliothèques de KDE et Qt. Cela améliore la portabilité de vos applications pour différents compilateurs qui ont des problèmes avec NULL.

Variables temporaires

Vous devriez toujours déclarer les instances temporaires avant de les utiliser. En général, cela est considéré comme étant meilleur. Par exemple :

// Ne faites pas :
for( int i=0; i<n; i++){
  // faire quelque chose
  };

// Mais écrivez :
int i;

for(i=0; i<n; i++){
  // faire quelque chose
  };

Cela est aussi valable pour les variables temporaires dans les appels de fonctions :

// Ne faites pas :
setColor( &(QColor(black)) );

// Mais écrivez :
QColor color(black);
setColor( &color );

15.2 Dépendances des Systèmes d'Exploitation

Comme les projets KDevelop utilisent les outils GNU pour créer les projets, vous êtes sûr que votre application fonctionnera sur presque tous les systèmes Unix. Cependant, vous pouvez rencontrer des problèmes lors de la compilation de votre application sur un autre Unix parce que les fichiers d'en-tête sont situés à des endroits différents ou bien que vous avez besoin d'une autre implantation, spécialement lorsque vous utilisez des fonctions de bas niveau de l'OS qui peuvent être différentes d'un système à l'autre.

En programmant avec C++ et Qt/KDE, vous avez du remarquer que les classes de Qt contiennent un grand ensemble de fonctionnalités qui sont déjà indépendantes du compilateur et de l'OS et rendent les choses plus simples, des chaînes de caractères (QString) à la lecture/écriture de fichiers (QFile) ; en utilisant Qt, vous rendez donc obsolètes la plupart des spécificités des systèmes d'exploitation.

Néanmoins, si vous utilisez Qt et que vous avez besoin d'utiliser des #defines pour votre application, vous devriez inclure qglobal.h et utiliser les #defines qui y sont déjà prédéfinis pour différents systèmes d'exploitation et compilateurs, comme ci-dessous.

Au lieu de laisser les distributeurs d'OS appliquer des correctifs à votre application (comme beaucoup le font pour construire des paquetages rpm ou autre), vous devriez utiliser des defines pour les sections qui sont spécifiques au système d'exploitation (mais vous n'avez pas besoin d'utiliser l'option -D à la compilation, les définitions de systèmes d'exploitation sont automatiquement prises en compte). La liste ci-dessous définit les systèmes disponibles et leurs définitions (les defines supplémentaires sont entre parenthèses) :

AIX :

#ifdef _AIX

BSDI Unix :

#if defined(bsdi) || defined(__bsdi__)

Dec Ultrix :

#if defined (ultrix) || defined(__ultrix) || defined(__ultrix__)

DG Unix :

#if defined(DGUX)

FreeBSD :

#ifdef __FreeBSD__

GNU Hurd :

#if defined(__GNU__)

HP-UX :

#if defined (hpux) || defined (__hpux) || defined (__hpux__)

Linux :

#if defined(linux) || defined(__linux) || defined(__linux__)

NetBSD :

#ifdef __NetBSD__

OpenBSD :

#ifdef __OpenBSD__

OSF Unix :

#if defined(__osf__)

QNX :

#if defined(__QNX__)

SCO UnixWare :

#if defined(_UNIXWARE)

SCO UnixWare 7 :

#if defined(sco) || defined(_UNIXWARE7)

SCO :

#if defined(_SCO_DS) || defined(M_UNIX) || defined(M_XENIX)

SGI Irix :

#if defined(sgi) || defined(__sgi)

SunOS :

#if defined (sun) || defined (__sun) || defined (__sun__)

Sun Solaris :

#if defined (_OS_SUN_) || defined (__SVR4)

Page suivante Page précédente Table des matières