Le langage D permet de faire de la programmation orientée objet. Pour cela, il faut passer par l'utilisation des classes. Il est important de distinguer la classe de l'objet. Par exemple, "Humain" est une classe et "Roger" est un objet. Ainsi, la classe est une définition générale ("Un humain est un mammifère avec 2 jambes, 2 bras, un nom et un âge.") alors que l'objet est le pendant concret de cette définition. On parle alors d'instance ("Roger" est une instance de la classe "Humain").
Exprimons cela en langage D
class Humain{
// Contenu de la classe
Par convention le nom d'une classe s'écrit avec la 1ère lettre en majuscule.
Les attributs.
Si on reprend l'exemple précédent, les attributs de la classe humain sont :
Soit :
class Humain
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
Vous remarquerez l'utilisation du mot private qui signifie que l'on ne peut accéder à tout ce qui suit depuis l'extérieur de l'objet, par opposition à public. Observez l'exemple qui suit :
class Humain
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
 public:
 getNbJambes(){
 return _nbJambes;
…
Humain robert = new Humain(); // création d'un objet robert de type Humain
uint nbJambes = robert._nbJambes; // Erreur: ceci ne peut pas fonctionner car _nbJambes est un attribut privé. On ne peut pas accéder à cette valeur.
uint nbJambes = robert.getNbJambes(); // renvoie le nombre de jambes de l'objet robert
Par convention, on préfixe les attributs par un tiret bas (underscore) : "_".
Les méthodes.
Les méthodes d'une classe correspondent aux actions. Par exemple, on peut implémenter la méthode "avancer". Pour cela, il faut stocker la position de l'humain, soit :
class Humain
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
 int _x;
 int _y;
 public:
 getNbJambes(){
 return _nbJambes;
 avancer(int x, int y){
 _x += x;
 _y += y;
…
Humain robert = new Humain(); // création d'un objet robert de type Humain
robert.avancer(2,3);
Dans cet exemple, il y a 2 méthodes :
On fait avancer robert de +2 et +3 sur la grille (abstrait).
Constructeur et Destructeur.
Les constructeurs et les destructeurs sont des méthodes particulières.
Constructeurs.
Le constructeur permet de construire un objet. La méthode utilisée s'appelle "this". Si l'on reprend l'exemple précédent :
class Humain
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
 int _x;
 int _y;
 public:
 getNbJambes()
 return _nbJambes;
 avancer(int x, int y)
 _x += x;
 _y += y;
 this(nbJambes, nbBras, nom, age, x, y)
 _nbJambes = nbJambes;
 _nbBras = nbBras;
 _nom = nom;
 _age = age;
 _x = x;
 _y = y;
…
Humain robert = new Humain(2, 2, "robert", 28, 3, 5); // création d'un objet robert de type Humain
Dans l'exemple précédent, on créé un objet robert de type Humain en précisant qu'il a 2 jambes, 2 bras, que son nom est robert, qu'il a 28 ans et se trouve en x:3 y:5.
Destructeurs.
Le destructeur est appelé par le ramasse-miette (« garbage collector ») pour libérer la mémoire et supprimer l'objet. On peut explicitement demander la destruction de l'objet avec le mot clé delete. Le nom de la méthode destructeur est ~this. Soit : 
class Humain
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
 int _x;
 int _y;
 public:
 getNbJambes()
 return _nbJambes;
 avancer(int x, int y){
 _x += x;
 _y += y;
 this(nbJambes, nbBras, nom, age, x, y)
 _nbJambes = nbJambes;
 _nbBras = nbBras;
 _nom = nom;
 _age = age;
 _x = x;
 _y = y;
 ~this(){
…
Humain robert = new Humain(2, 2, "robert", 28, 3, 5); // création d'un objet robert de type Humain
delete robert;
Si on ne supprime pas l'objet robert explicitement avec le mot clé delete le ramasse-miette (garbage collector) le supprimera tout seul quand l'objet ne sera plus utilisé. On peut donc choisir de gérer ou de ne pas gérer la mémoire.
Vous avez remarqué le destructeur est vide, en effet aucune variable a été créé par le mot clé new par conséquent le garbage collecteur pourra faire son travail correctement quand il sera appelé.
L'héritage.
L'héritage consiste à spécialiser une classe : par exemple, un magicien est un humain. Afin d'éviter de réécrire le code de la classe humain, on réutilise le code et on effectue un héritage, de la manière suivante :
class Magicien : Humain
private:
 uint _mana;
public:
 this(uint mana,char[] nom, uint age, x, y)
 super(2, 2, nom, age, x, y); // construction de l'humain
 _mana = mana;
 ~this()
Magicien gandalf = new(200, "gandalf", 7000, 156, -54);
Vous remarquerez l'utilisation du mot-clé super celui-ci permet d'appeler le constructeur de la classe mère (c'est à dire ici le constructeur Humain). Pour construire un magicien on doit lui donner une quantité de mana (pouvoirs magiques), un nom, un age et sa position dans l'espace.
Les Classes Abstraites.
Une classe abstraite est une classe que l'on ne peut pas instancier (c'est à dire dont on ne peut créer un objet). Par exemple, on peut décider que la classe mammifère est abstraite. En effet, un mammifère en tant que tel n'existe pas (par exemple, l'Homme est un mammifère et n'existe qu'en tant qu'homme).
La mise en place d'une classe abstraite se fait de la manière suivante :
abstract class Mammifère
 private:
 uint _temperatureCorporelle;
 uint _taille;
 char[] _espece;
 public:
 this(uint temperatureCorporelle, uint taille, char[] espece)
 _temperatureCorporelle = temperatureCorporelle;
 _taille = taille;
 _espece = espece;
 ~this()
 getTemperatureCorporelle()
 return _temperatureCorporelle;
 getTaille()
 return _taille;
 getEspece()
 return _espece;
Ainsi, on ne pourra pas écrire :
Mammifere homme = new Mammifere(37, 170, "Homo sapiens");
Mais on devra créer une classe Homme qui héritera de la classe abstraite Mammifère.
Les interfaces.
Le langage D ne supporte pas l'héritage multiple. Pour contourner cela, on utilise les interfaces. Une interface liste des fonctions que doit obligatoirement utiliser une classe. Reprenons la classe Humain et demandons à celle ci d'implémenter les méthodes suivantes:
Vous en conviendrez, ces méthodes peuvent-être utilisées par d'autres classes que les Humains mais par l'utilisation d'une interface, on standardise le nom de ces méthodes et on évitera ainsi de se retrouver avec du code comme :
On déclare une interface comme cela :
interface actions{
 void seReposer();
 void esquiver();
 void marcher();
 void courir();
Et on l'implémente à une classe ainsi :
class Humain : actions, Mammifere
 private:
 uint _nbJambes;
 uint _nbBras;
 char[] _nom;
 uint _age;
 int _x;
 int _y;
 public:
 getNbJambes()
 return _nbJambes;
 avancer(int x, int y)
 _x += x;
 _y += y;
 this(nbJambes, nbBras, nom, age, x, y)
 super(37, 170, "Homo sapiens");
 _nbJambes = nbJambes;
 _nbBras = nbBras;
 _nom = nom;
 _age = age;
 _x = x;
 _y = y;
 ~this(){
 void seReposer(){
 void esquiver(){
 void marcher(){
 void courir(){
Je vous invite également à lire le tutoriel traitant de la notion d'interface sur le site du zero (bien que le tutoriel en question soit rédigé pour java, la syntaxe est proche et l'utilisation des interfaces est identique).
Les exceptions.
La gestion des erreurs en D est syntaxiquement identique à celle de java. Par conséquent, vous pouvez jeter un œil sur le site du zero pour en apprendre plus.
Le langage D propose une classe Exception que vous pourrez proposer en héritage à vos propres classes de gestion d'erreur. Vous trouverez la définition de cette classe dans /usr/include/d/object.di . Cette classe étant définie dans object.d, elle n'a pas besoin d'être importé.
Pour commencer, nous allons voir un cas plus simple, celui du dépassement de la taille d' un tableau :
import tango.core.Exception;
import tango.io.Stdout;
void main()
 try 
 int[5] array;
 for (uint i = 0; i <= 10; i++)
 array[i] = 5;
 catch (ArrayBoundsException e)
 Stdout("Vous avez dépassé la taille du tableau !").nl;
 Stdout.formatln("Erreur : {}", e.toString);
On définit un tableau de 5 éléments et on essaye d'aller sur le 6ème (cela déclenche l'erreur) :
Note : la définition de l'exception ArrayBoundsException se trouve dans le module tango.core.Exception (c'est pour cela qu'on l'importe ici).
Voyons maintenant le cas de la division par zero, avec une classe Exception personnalisée :
import tango.io.Stdout;
class DivideByZeroException : Exception 
 this( char[] file, long line ){
 super("Division par zero interdite !", file, line );
void main()
 float a = 3;
 float b = 0;
 float c;
 try
 if (b ==0)
 throw new DivideByZeroException(__FILE__,__LINE__);
 c = a/b;
 catch (DivideByZeroException e)
 Stdout("Division par zero impossible !").nl;
 Stdout.formatln("Erreur: {}", e.toString);
Les templates.
Un template permet d'écrire une fonction ou une classe qui peut accepter n'importe quel type de paramètre et qui retourne également n'importe quel type de variable. Prenons en exemple une fonction pour additionner deux nombres. Traditionnellement, vous feriez :
int addition (int a, int b)
 return a + b;
float addition(float a, float b)
return a + b;
float addition(int a, float b)
return a + b;
et ainsi de suite. On remarquera que c'est fastidieux et pour pas grand chose !
En D, avec la notion de template, on peut simplifier cela de la manière suivante :
typeof(T + U) Addition(T = typeof(element1), U = typeof(element2))(T element1, U element2)
 return element1 + element2;
Explication :
Exemple :
import tango.io.Stdout;
typeof(T + U) Addition(T = typeof(element1), U = typeof(element2))(T element1, U element2)
 return element1 + element2;
void main()
 int a = 3;
 int b = 5;
 float c = 3.14;
 Stdout.formatln("{} + {} = {}", a, b, Addition !()(a,b));
 Stdout.formatln("{} + {} = {}", a, c, Addition !()(a,c));
Note: un template se remarque par identifiant!(type)(parametres)
Ici, nous n'avons pas précisé de type, le template prendra donc les types par défaut de T et U. On pourrait demander, lorsque l'on additionne un entier par un flottant de considérer le dernier comme un entier.
Pour cela, il nous suffit de changer la dernière ligne :
Stdout.formatln("{} + {} = {}", a, c, Addition!()(a,b));
Stdout.formatln("{} + {} = {}", a, c, Addition!(uint, uint)(a,c));
Et d'observer le résultat !