346 votes

Quand faut-il utiliser le terme "ami" en C++ ?

J'ai lu le document FAQ C++ et j'étais curieux de connaître le friend déclaration. Je ne l'ai personnellement jamais utilisé, mais je suis intéressé par l'exploration de ce langage.

Quel est un bon exemple d'utilisation friend ?


En lisant la FAQ un peu plus longuement, j'aime bien l'idée de la << >> surcharge des opérateurs et ajout en tant qu'ami de ces classes. Cependant, je ne suis pas sûr que cela ne brise pas l'encapsulation. Quand ces exceptions peuvent-elles rester dans la rigueur de la POO ?

327voto

Andrew Grant Points 35305

Tout d'abord (IMO) n'écoutez pas les personnes qui disent friend n'est pas utile. Il EST utile. Dans de nombreuses situations, vous aurez des objets contenant des données ou des fonctionnalités qui ne sont pas destinées à être accessibles au public. C'est particulièrement vrai dans le cas de bases de code importantes avec de nombreux auteurs qui peuvent n'avoir qu'une connaissance superficielle des différents domaines.

Il existe des alternatives au spécificateur friend, mais elles sont souvent encombrantes (classes concrètes au niveau cpp/types masqués) ou pas infaillibles (commentaires ou conventions de noms de fonctions).

Sur la réponse ;

En friend permet à la classe désignée d'accéder à des données ou à des fonctionnalités protégées au sein de la classe qui fait la déclaration d'ami. Par exemple, dans le code ci-dessous, n'importe qui peut demander à un enfant son nom, mais seuls la mère et l'enfant peuvent changer le nom.

Vous pouvez aller plus loin dans cet exemple simple en considérant une classe plus complexe telle qu'une fenêtre. Il est fort probable qu'une fenêtre possède de nombreux éléments de fonction/données qui ne doivent pas être accessibles au public, mais qui sont nécessaires à une classe connexe telle que WindowManager.

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};

155voto

Daemin Points 5651

Au travail, nous utiliser des amis pour tester le code de manière extensive. Cela signifie que nous pouvons fournir une encapsulation et un masquage de l'information appropriés pour le code principal de l'application. Mais nous pouvons aussi avoir un code de test séparé qui utilise des amis pour inspecter l'état interne et les données pour les tests.

Il suffit de dire que je n'utiliserais pas le mot-clé "ami" comme un élément essentiel de votre conception.

92voto

En friend Le mot-clé a un certain nombre de bons usages. Voici les deux utilisations qui me paraissent les plus évidentes :

Définition de l'ami

La définition amicale permet de définir une fonction dans la portée de la classe, mais la fonction ne sera pas définie comme une fonction membre, mais comme une fonction libre de l'espace de nom qui l'entoure, et ne sera pas visible normalement, sauf pour la recherche dépendante de l'argument. Cela le rend particulièrement utile pour la surcharge d'opérateurs :

namespace utils {
    class f {
    private:
        typedef int int_type;
        int_type value;

    public:
        // let's assume it doesn't only need .value, but some
        // internal stuff.
        friend f operator+(f const& a, f const& b) {
            // name resolution finds names in class-scope. 
            // int_type is visible here.
            return f(a.value + b.value);
        }

        int getValue() const { return value; }
    };
}

int main() {
    utils::f a, b;
    std::cout << (a + b).getValue(); // valid
}

Classe de base privée CRTP

Parfois, on constate qu'une politique a besoin d'accéder à la classe dérivée :

// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
    void doSomething() {
        // casting this to Derived* requires us to see that we are a 
        // base-class of Derived.
        some_type const& t = static_cast<Derived*>(this)->getSomething();
    }
};

// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
    // we derive privately, so the base-class wouldn't notice that, 
    // (even though it's the base itself!), so we need a friend declaration
    // to make the base a friend of us.
    friend class SomePolicy<FlexibleClass>;

    void doStuff() {
         // calls doSomething of the policy
         this->doSomething();
    }

    // will return useful information
    some_type getSomething();
};

Vous trouverez un exemple non-contourné pour cela dans este réponse. Un autre code utilisant cela se trouve dans este réponse. La base de la CRTP transfère son pointeur, afin de pouvoir accéder aux champs de données de la classe dérivée à l'aide de pointeurs de membres de données.

41voto

Konrad Rudolph Points 231505

@roo : L'encapsulation n'est pas rompue ici car la classe elle-même dicte qui peut accéder à ses membres privés. L'encapsulation ne serait rompue que si cela pouvait être causé depuis l'extérieur de la classe, par exemple si votre operator << proclamerait "Je suis un ami de la classe foo ."

friend remplace l'utilisation de public pas l'utilisation de private ¡!

En fait, la FAQ C++ répond déjà à cette question .

27voto

Mark Harrison Points 77152

L'exemple canonique est la surcharge de l'opérateur<<. Une autre utilisation courante est de permettre à une classe d'aide ou d'administration d'accéder à vos internes.

Voici quelques directives dont j'ai entendu parler par des amis du C++. La dernière est particulièrement mémorable.

  • Vos amis ne sont pas les amis de votre enfant.
  • Les amis de votre enfant ne sont pas vos amis.
  • Seuls les amis peuvent toucher vos parties intimes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X