web-dev-qa-db-fra.com

Quels opérateurs doivent être déclarés amis?

Dans certains livres et souvent sur Internet, je vois des recommandations comme "operator== doit être déclaré ami ".

Comment dois-je comprendre quand un opérateur doit être déclaré ami et quand il doit être déclaré membre? Quels sont les opérateurs qui devront le plus souvent être déclarés amis en plus de == et <<?

34

Cela dépend vraiment si une classe va être à gauche ou à droite de l'appel à operator== (ou autre opérateur). Si une classe doit se trouver du côté droit de l'expression - et ne fournit pas de conversion implicite en un type comparable à celui de gauche - vous devez implémenter operator== comme fonction distincte ou comme friend de la classe. Si l'opérateur doit accéder aux données de classe privée, il doit être déclaré comme friend.

Par exemple,

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
};

vous permet de comparer un message à une chaîne

Message message("Test");
std::string msg("Test");
if (message == msg) {
    // do stuff...
}

mais pas l'inverse

    if (msg == message) { // this won't compile

Vous devez déclarer un ami operator== à l'intérieur de la classe

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    friend bool operator==(const std::string& lhs, const Message& rhs);
};

o déclarer un opérateur de conversion implicite au type approprié

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    operator std::string() const;
};

o déclarer une fonction distincte, qui n'a pas besoin d'être un ami si elle n'accède pas aux données de classe privée

bool operator==(const std::string& lhs, const Message& rhs);
43
Chris Frederick

Lorsque vous avez vos opérateurs en dehors de la classe, les deux paramètres peuvent participer à des conversions de type implicites (alors qu'avec les opérateurs étant définis dans le corps de la classe, seuls les opérandes de droite le peuvent). En général, c'est un avantage pour tous les opérateurs binaires classiques (c'est-à-dire ==, !=, +, -, <<, ...).

Bien sûr, vous ne devez déclarer les opérateurs friends de votre classe que si vous en avez besoin et non s'ils calculent leur résultat uniquement sur la base des membres publics de la classe.

19

En règle générale, seuls les opérateurs qui sont implémentés en tant que fonctions libres qui ont véritablement besoin d'accéder aux données privées ou protégées de la classe sur laquelle ils opèrent doivent être déclarés amis, sinon ils devraient simplement être des fonctions non-amis non-membres.

Généralement, les seuls opérateurs que j'implémente en tant que fonctions membres sont ceux qui sont fondamentalement asymétriques et où les opérandes n'ont pas de rôles équivalents. Ceux que j'ai tendance à implémenter en tant que membres sont ceux qui doivent être membres: simple affectation, (), [] et -> avec des opérateurs d'affectation composés, des opérateurs unaires et peut-être quelques surcharges de << et >> pour les classes qui sont elles-mêmes des classes de type stream ou stream. Je ne surcharge jamais &&, || ou ,.

Tous les autres opérateurs que j'ai tendance à implémenter comme des fonctions libres, en utilisant de préférence l'interface publique des classes sur lesquelles ils opèrent, ne redeviennent amis qu'en cas de besoin.

Garder des opérateurs tels que !=, ==, <, +, /, etc. en tant que fonctions non membres permet un traitement identique des opérandes gauche et droit par rapport aux séquences de conversion implicites, ce qui contribue à réduire le nombre d'asymétries surprenantes.

8
CB Bailey