72 votes

Méthodes d'extension en c ++

J'étais à la recherche d'une mise en œuvre de méthodes d'extension en c++ et est venu sur cette maquette.std.c++ discussion qui mentionne qu' polymorphic_map peut être utilisé pour les méthodes associées à la classe, mais le lien semble mort. Personne ne sait ce que cette réponse faisait allusion, ou si il y a un autre moyen d'étendre les classes d'une manière similaire à des méthodes d'extension (peut-être à travers un certain usage de mixin?).

Je sais canonique de C++ solution est d'utiliser des fonctions libres; ce n'est plus par curiosité qu'autre chose.

76voto

Les différentes langues de l'approche de développement de différentes façons. En particulier, C# et Java ont un fort point de vue par rapport à OO qui mène à tout est un objet état d'esprit (le C# est un peu plus laxiste ici). Dans cette approche, les méthodes d'extension fournissent un moyen simple pour l'extension d'un objet existant ou de l'interface pour ajouter de nouvelles fonctionnalités.

Il n'existe pas de méthodes d'extension en C++, ils ne sont pas nécessaires. Lors de l'élaboration de C++, oubliez le tout est un objet paradigme-qui, en passant, est faux, même en Java/C# [*]. Un autre état d'esprit est pris en C++, il y a des objets, et les objets ont des activités qui sont intrinsèquement partie de l'objet, mais il y a aussi d'autres activités qui font partie de l'interface et n'a pas besoin d'être partie de la classe. A lire absolument par Herb Sutter est Ce qui est Dans une Classe?, où l'auteur défend (et je suis d'accord) que vous pouvez facilement étendre une classe avec de simples fonctions libres.

Comme un exemple simple, le standard basé sur un modèle de classe basic_ostream a quelques méthodes pour vider le contenu de certains types primitifs, et puis elle est renforcée avec (également basé sur un modèle) gratuit fonctions étendre cette fonctionnalité à d'autres types de publics actuels de l'interface. Par exemple, std::cout << 1; est mis en œuvre en tant que membre de la fonction, tandis que d' std::cout << "Hi"; gratuit) est une fonction implémentée en d'autres termes, plus les membres de base.

Extensibilité en C++ est obtenu par le biais de fonctions libres, non pas par des moyens d'ajouter de nouvelles méthodes à des objets existants.

[*] Tout est pas un objet.

Dans un domaine donné contient un ensemble d'objets réels qui peuvent être modélisées et les opérations qui peuvent être appliquées, dans certains cas, ces opérations feront partie de l'objet, mais dans certains autres cas, ils ne le seront pas. En particulier, vous trouverez l'utilitaire de classes dans les langues qui prétendent que tout est un objet et ceux de l'utilitaire de classes ne sont rien, mais une couche en essayant de cacher le fait que ces méthodes n'appartiennent pas à un objet particulier.

Même certaines opérations qui sont mises en œuvre que les fonctions de membres ne sont pas vraiment des opérations sur les objets. Envisager l'ajout d'un Complex nombre de classe, comment est - sum (ou +) plus d'une opération sur le premier argument de la seconde? Pourquoi a.sum(b); ou b.sum(a), ne devrait-elle pas être sum( a, b )?

Forcer les opérations à être membre de méthodes réellement produit des effets bizarres-mais nous sommes habitués à eux: a.equals(b); et b.equals(a); pourrait avoir des résultats complètement différents, même si la mise en œuvre de l' equals est entièrement symétrique. (Pensez à ce qui se passe lorsque a ou b est un pointeur null)

33voto

Akira Takahashi Points 919

L'approche de Boost Range Library utilise l'opérateur | ().

 r | filtered(p);
 

Je peux également écrire le trim pour la chaîne de la même manière.

 #include <string>

namespace string_extension {

struct trim_t {
    std::string operator()(const std::string& s) const
    {
        ...
        return s;
    }
};

const trim_t trim = {};

std::string operator|(const std::string& s, trim_t f)
{
    return f(s);
}

} // namespace string_extension

int main()
{
    const std::string s = "  abc  ";

    const std::string result = s | string_extension::trim;
}
 

13voto

dennis90 Points 54

C'est la chose la plus proche que j'ai jamais vu de méthodes d'extension en C++. Personnellement, j'aime la façon dont il peut être utilisé, et, éventuellement, c'est le plus proche que nous pouvons obtenir à des méthodes d'extension dans cette langue. Mais il y a quelques inconvénients:

  • Il peut être compliqué à mettre en œuvre
  • La priorité de l'opérateur peut être pas gentil de temps en temps, cela peut causer des surprises

Une solution:

#include <iostream>

using namespace std;


class regular_class {

    public:

        void simple_method(void) const {
            cout << "simple_method called." << endl;
        }

};


class ext_method {

    private:

        // arguments of the extension method
        int x_;

    public:

        // arguments get initialized here
        ext_method(int x) : x_(x) {

        }


        // just a dummy overload to return a reference to itself
        ext_method& operator-(void) {
            return *this;
        }


        // extension method body is implemented here. The return type of this op. overload
        //    should be the return type of the extension method
        friend const regular_class& operator<(const regular_class& obj, const ext_method& mthd) {

            cout << "Extension method called with: " << mthd.x_ << " on " << &obj << endl;
            return obj;
        }
};


int main()
{ 
    regular_class obj;
    cout << "regular_class object at: " << &obj << endl;
    obj.simple_method();
    obj<-ext_method(3)<-ext_method(8);
    return 0;
}

Ce n'est pas mon invention, récemment, un ami à moi l'a postée, il m'a dit qu'il l'a obtenu d'une université, liste de diffusion.

9voto

Baltasarq Points 5164

La réponse courte est que vous ne pouvez pas le faire. La réponse longue est que vous pouvez le simuler, mais sachez que vous aurez à créer beaucoup de code sous la forme d'une solution (en fait, je ne pense pas qu'il y est une solution élégante).

Dans la discussion, très complexe de la solution de contournement est fournie à l'aide de l'opérateur (ce qui est une mauvaise idée, à mon avis). Je suppose que la solution fournie dans le lien mort était plus ou moins similaire (car il était basé sur l'opérateur|).

Ceci est basé sur la capacité d'être en mesure de faire plus ou moins la même chose qu'une méthode d'extension avec les opérateurs. Par exemple, si vous voulez surcharger le ostream de l'opérateur<< pour votre nouvelle classe Foo, vous pourriez faire:

class Foo {
    friend ostream &operator<<(ostream &o, const Foo &foo);
    // more things...
};

ostream &operator<<(ostream &o, const Foo &foo)
{
  // write foo's info to o
}

Comme je l'ai dit, c'est le seul mécanisme similaire disponible en C++ pour les méthodes d'extension. Si vous pouvez naturellement vous traduire votre fonction pour un opérateur surchargé, il est bien. La seule autre possibilité est artificiellement la surcharge d'un opérateur qui n'a rien à voir avec votre objectif, mais cela va vous faire écrire en peu de code source de confusion.

La plupart approche similaire, je pense, de dire à créer une classe d'extension et de créer votre nouvelles méthodes. Malheureusement, cela signifie que vous aurez besoin de "s'adapter" vos objets:

class stringext {
public:
    stringext(std::string &s) : str( &s )
        {}
    string trim()
        {  ...; return *str; }
private:
    string * str;
};

Et puis, quand vous voulez faire les choses:

void fie(string &str)
{
    // ...
    cout << stringext( str ).trim() << endl;
}

Comme l'a dit, ce n'est pas parfait, et je ne pense pas que ce genre de solution parfaite existe. Désolé.

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