IV. L'agrégation (1ère partie)

Hep la ! j'avais promis que nous causerions rapidement de l'héritage et du polymorphisme et me voila en train d'attaquer l'agrégation ! N'ayez crainte nous reviendrons prochainement sur l'héritage, nous ne faisons qu'un court crochet par l'agrégation que pour respecter une progression dans la difficulté des concepts étudiés.

Nous allons dorénavant étudier la relation d'aggrégation entre objets. Prenons l'exemple classique des objets graphiques qui nous servira également à étudier la relation d'héritage ainsi que le polymorphisme.

Plan du chapitre :

  1. Agrégation interne
  2. Agrégation externe

Liste des programmes :

Liste des tableaux :

4.1 Agrégation interne

On peut considérer que la représentation d'un objet graphique repose sur un point de base, qui pourra être l'une des extrémités pour une ligne, le centre pour un cercle ou le coin inférieur gauche pour un rectangle.

Comme nous disposons d'une classe Point, nous allons agréger un objet de type Point dans chaque objet de la classe ObjetGraphique. Pour l'instant considérons la déclaration minimaliste suivante pour la classe ObjetGraphique :

class ObjetGraphique
{
  public:
    ObjetGraphique(int x, int y, int couleur, int epaisseur);

  private:
    Point pointBase_;
    int   couleur_;
    int   epaisseur_;
};

...

ObjetGraphique::ObjetGraphique(int x, int y, int couleur, int epaisseur) :
  pointBase_(x,y),
  couleur_(couleur),
  epaisseur_(epaisseur)
{}

Programme 4.1 Agrégation d'un objet de classe Point dans une objet de classe ObjetGraphique

Le constructeur de la classe ObjetGraphique commence par initialiser l'objet pointBase_ en appelant son constructeur avant d'initialiser les attributs atomiques couleur_ et epaisseur_.

Très important : s'il est possible d'affecteur leur valeur aux attributs atomiques à l'intérieur du corps du constructeur, il est obligatoire d'initialiser les attributs objets dans la liste d'initialisation du constructeur à moins que l'objet ne dispose d'un constructeur par défaut, auquel cas, celui-ci sera appelé avant l'exécution du corps du constructeur.

Si vous ne disposez pas d'un constructeur par défaut ou ne pouvez pas faire l'initialisation de l'objet dans la liste d'initialisation, car, par exemple, elle nécessite des calculs importants, alors, vous serez obligés de recourir à un pointeur comme dans l'exemple suivant :

class ObjetGraphique
{
  public:
    ObjetGraphique(int x, int y, int couleur, int epaisseur);

  private:
    int    couleur_;
    int    epaisseur_;
    Point *pointBase_;
};

...

ObjetGraphique::ObjetGraphique(int x, int y, int couleur, int epaisseur) :
  couleur_(couleur),
  epaisseur_(epaisseur)
{
  pointBase_=new Point(x,y);
}

// N'oubliez pas de détruire l'objet créé dans le destructeur

~ObjetGraphique(void)
{
  delete pointBase_;
}

Programme 4.2 Agrégation interne par pointeur

Jusqu'ici, nous avons considéré que l'objet pointBase_ est créé dans le constructeur de la classe ObjetGraphique. C'est de l'agrégation interne. Considérons une autre possibilité (dite agrégation externe) : l'objet est créé à l'extérieur de la classe et n'est que passé au constructeur.

Agrégation externe

Contrairement à l'agrégation iterne où l'objet agrégé est créé par l'objet agrégateur, l'agrégation externe repose sur l'utilisation d'un objet en provenance de l'extérieur. Vous avez alors 2 possibilités : agréger par référence ou par pointeur.

Si vous agrégez par référence, la référence doit impérativement être initialisée dans la liste d'initialisation comme dans l'exemple suivant :

class ObjetGraphique  
{
  public:
	  ObjetGraphique(Point &p,
                   int couleur,
                   int epaisseur) : 
      pointBase_(p)
    {      
      couleur_=couleur;
      epaisseur_=epaisseur;
    }
	  
  private:
	   int    epaisseur_;
   	int    couleur_;
    Point &pointBase_;
};

Programme 4.3 Agrégation externe par référence

Sinon, une fois de plus, vous devrez utiliser un pointeur :

class ObjetGraphique  
{
  public:
	  ObjetGraphique(Point &p,
                   int couleur,
                   int epaisseur) 
    {            
      couleur_=couleur;
      epaisseur_=epaisseur;
      pointBase_=&p;
    }
	  
  private:
	   int    epaisseur_;
  	 int    couleur_;
    Point *pointBase_;
};

Programme 4.4 Agrégation externe par pointeur

Attention ! si vous agrégez un objet en provenance de l'extérieur, ce n'est probablement pas à vous de le détruire. En effet, une règle habituelle dit que le créateur d'un objet est le premier responsable de sa destruction.

4.3 Donner accès à l'objet agrégé

Dans de nombreux cas, vous aurez envie de renvoyer accès à un objet agrégé. Et dans la plupart des cas, vous ne voudrez donner qu'un accès en lecture. Le plus simple dans ce cas est de renvoyer une référence constante. Reprennons l'exemple de l'objet graphique dans les trois cas suivants :

private:
  Point pointBase_;
const Point &ObjetGraphique::pointDeBase(void) const
{
  return pointBase_;
}
private:
  Point &pointBase_;
const Point &ObjetGraphique::pointDeBase(void) const
{ 
  return pointBase_;
}
private:
  Point *pointBase_;
const Point &ObjetGraphique::pointDeBase(void) const
{
  return *pointBase_;
}

Tableau 4.1 Renvoyer une référence constante sur un objet agrégé