136 votes

Polymorphisme en c ++

Autant que je sache:

C++ offre trois différents types de polymorphisme.

  • Fonctions virtuelles
  • Nom de la fonction de surcharge
  • La surcharge d'opérateur

En plus de ces trois types de polymorphisme, il existe d'autres types de polymorphisme:

  • au moment de l'exécution
  • au moment de la compilation
  • ad-hoc polymorphisme
  • polymorphisme paramétrique

Je sais que polymorphisme d'exécution peut être réalisé par des fonctions virtuelles et polymorphisme statique peut être obtenue par le modèle de fonctions

Mais pour les deux autres

ad-hoc polymorphisme:

Si la gamme des types réels qui peuvent être utilisés est limité et les combinaisons doivent être spécifiées individuellement avant de les utiliser, ce qui est appelé ad-hoc polymorphisme.

polymorphisme paramétrique:

Si tout le code est écrit sans la mention d'un type spécifique et peut donc être utilisé de façon transparente avec n'importe quel nombre de nouveaux types, il est appelé polymorphisme paramétrique.

j'ai du mal à comprendre :(

quelqu'un peut-il expliquer à la fois si possible avec un exemple? J'espère que les réponses à ces questions serait utile pour de nombreux nouveaux passouts de leurs collèges.

15voto

Steve314 Points 12599

En C++, la distinction importante est le temps d'exécution de vs au moment de la compilation de liaison. Ad-hoc vs paramétrique n'aide pas vraiment, comme je l'expliquerai plus tard.

|----------------------+--------------|
| Form                 | Resolved at  |
|----------------------+--------------|
| function overloading | compile-time |
| operator overloading | compile-time |
| templates            | compile-time |
| virtual methods      | run-time     |
|----------------------+--------------|

Remarque - polymorphisme de l'exécution peut encore être résolu à la compilation, mais c'est juste de l'optimisation. Avoir besoin d'un soutien au moment de l'exécution de la résolution de manière efficace, et en échange contre d'autres questions, fait partie de ce qui a conduit à des fonctions virtuelles étant ce qu'elles sont. Et c'est vraiment la clé pour toutes les formes de polymorphisme en C++ - chaque provient de différents ensembles de compromis faite dans un contexte différent.

Surcharge de fonctions et la surcharge d'opérateur sont la même chose dans tous les sens qui compte. Le nom et la syntaxe pour leur utilisation n'affecte pas le polymorphisme.

Les modèles vous permettent de spécifier les lots de la fonction des surcharges à la fois.

Il y a un autre ensemble de noms pour la même résolution, le temps d'idée...

|---------------+--------------|
| early binding | compile-time |
| late binding  | run-time     |
|---------------+--------------|

Ces noms ne sont plus associés à la programmation orientée objet, donc c'est un peu bizarre de dire qu'un modèle ou d'autres non-membre de la fonction utilise la liaison anticipée.

Pour mieux comprendre la relation entre les fonctions virtuelles et de surcharge de fonctions, il est également utile de comprendre la différence entre "envoi" et "multiple dispatch". L'idée peut être comprise comme une progression...

  • Tout d'abord, il y a monomorphe fonctions. La mise en œuvre de la fonction est identifiée de manière unique par le nom de la fonction. Aucun des paramètres spéciaux.
  • Ensuite, il existe une seule expédition. L'un des paramètres est considéré comme spécial, et utilisé (avec le nom) pour identifier la mise en œuvre d'utilisation. En programmation orientée objet, nous avons tendance à penser de ce paramètre comme "l'objet", la liste avant le nom de la fonction, etc.
  • Ensuite, il y a de multiples expédition. Tous les paramètres de contribuer à l'identification de l'implémentation à utiliser. Donc, une fois de plus, aucun des paramètres doit être spécial.

Il y a évidemment plus de la programmation orientée objet que d'une excuse pour désigner un paramètre spécial, mais c'est une partie de lui. Et concernant ce que je disais à propos de compromis,- envoi qui est assez facile à faire efficacement (l'habitude de la mise en œuvre est appelée "tables virtuelles"). La répartition Multiple est plus maladroit, et pas seulement en termes d'efficacité, mais aussi pour la compilation séparée. Si vous êtes curieux, vous pouvez rechercher ", l'expression "problème".

Tout comme il est un peu étrange d'utiliser le terme "liaison anticipée" pour les non-membres fonctions, c'est un peu étrange d'utiliser les termes "envoi" et "multiple dispatch" où le polymorphisme est résolu au moment de la compilation. Habituellement, C++ est considérée comme n'ayant pas la répartition multiple, qui est considéré comme un type particulier de l'exécution de la résolution. Cependant, la fonction de surcharge peut être vu comme multiple à répartition effectuée au moment de la compilation.

Pour en revenir à paramétrique vs ad-hoc polymorphisme, ces termes sont de plus en plus populaire dans la programmation fonctionnelle, et ils n'ont pas assez de travail en C++. Même si...

Polymorphisme paramétrique signifie que vous avez les types de paramètres, et exactement le même code est utilisé quel que soit le type que vous utilisez pour ces paramètres.

Ad-hoc polymorphisme ad-hoc dans le sens que vous fournir un code différent selon les types.

Les surcharges et les fonctions virtuelles sont deux exemples d'ad-hoc polymorphisme.

Encore une fois, il y a quelques synonymes...

|------------+---------------|
| parametric | unconstrained |
| ad-hoc     | constrained   |
|------------+---------------|

Sauf que ce ne sont pas tout à fait synonymes, mais ils sont généralement traitées comme si elles étaient, et c'est là que la confusion est susceptible de se produire en C++.

Le raisonnement derrière traiter ces comme synonymes est qu'en restreignant le polymorphisme particulier les classes de types, il devient possible d'utiliser des opérations spécifiques à ces classes de types. Le mot "classes" ici peuvent être interprétés dans le sens de la programmation orientée objet, mais vraiment juste fait référence (généralement le nom) les ensembles de types qui partagent certaines opérations.

Afin de polymorphisme paramétrique est généralement pris (au moins par défaut) pour impliquer sans contrainte polymorphisme. Parce que le même code est utilisé quel que soit le type de paramètres, la seule supportable opérations sont ceux qui fonctionnent pour tous les types. En laissant l'ensemble des types sans contrainte, vous limiter sévèrement l'ensemble des opérations que vous pouvez appliquer à ces types.

En Haskell par exemple, vous pouvez avoir...

myfunc1 :: Bool -> a -> a -> a
myfunc1 c x y = if c then x else y

L' a ici est une contrainte de type polymorphe. Il pourrait être n'importe quoi, donc il n'y a pas beaucoup que nous pouvons faire avec les valeurs de ce type.

myfunc2 :: Num a => a -> a
myfunc2 x = x + 3

Ici, a est contraint d'être un membre de l' Num classe - types qui agissent comme des numéros. Cette contrainte permet de faire le numéro-ish choses avec ces valeurs, telles que ajouter. Même l' 3 est polymorphe - l'inférence de type chiffres que vous voulez dire que l' 3 de type a.

Je pense à cela comme une contrainte polymorphisme paramétrique. Il n'y a qu'une mise en œuvre, mais elle ne peut être appliquée dans la limité de cas. Le ad-hoc aspect est le choix de la + et 3 à utiliser. Chaque "instance" d' Num a c'est propre, distincte de la mise en œuvre de ces. Donc, même en Haskell "paramétrique" et "sans contrainte" ne sont pas vraiment synonymes - ne me blâmez pas, c'est pas de ma faute!

En C++, à la fois les surcharges et les fonctions virtuelles sont ad-hoc polymorphisme. La définition de l'ad-hoc polymorphisme ne se soucie pas de savoir si la mise en œuvre est sélectionnée au moment de l'exécution ou au moment de la compilation.

C++ est très proche de polymorphisme paramétrique avec des modèles si chaque paramètre du modèle est de type typename. Il y a des paramètres de type, et il y a un unique de mise en œuvre n'importe quels types sont utilisés. Cependant, la Substitution "l'Échec n'Est Pas Une Erreur" la règle signifie que les contraintes implicites résulter de l'utilisation des opérations dans le modèle. Des complications supplémentaires comprennent la spécialisation de modèle pour fournir une alternative aux modèles différents (ad-hoc), les implémentations.

Donc dans un sens C++ a du polymorphisme paramétrique, mais il est implicitement limité et pourrait être remplacée par une ad-hoc, des solutions de rechange - c'est à dire cette classification ne fonctionne pas vraiment pour le C++.

2voto

Eric Z Points 7160

Comme ad-hoc polymorphisme, cela signifie que la fonction de la surcharge ou de la surcharge d'opérateur. Découvrez ici:

http://en.wikipedia.org/wiki/Ad-hoc_polymorphism

De polymorphisme paramétrique, le modèle, les fonctions peuvent également être prises en compte parce qu'ils ne sont pas forcément des paramètres FIXES. Par exemple, une fonction peut trie un tableau d'entiers et il peut également trier tableau de chaînes de caractères, etc.

http://en.wikipedia.org/wiki/Parametric_polymorphism

2voto

Joe Points 406

Cela peut ne pas être d'une grande aide, mais j'ai fait ce présenter mes amis à la programmation en accordant des fonctions définies, comme START, et END pour la fonction principale de sorte qu'il n'était pas trop intimidant (ils utilisé la main.cpp fichier). Il contient des Polymorphes classes et les structures, les modèles, les vecteurs, les matrices, preproccessor directives, l'amitié, les opérateurs et les pointeurs (tout ce dont vous devez probablement le savoir avant d'essayer polymorphisme):

Remarque: Il n'est pas terminé, mais vous pouvez obtenir l'idée

main.cpp

#include "main.h"
#define ON_ERROR_CLEAR_SCREEN false
START
    Library MyLibrary;
    Book MyBook("My Book", "Me");
    MyBook.Summarize();
    MyBook += "Hello World";
    MyBook += "HI";
    MyBook.EditAuthor("Joe");
    MyBook.EditName("Hello Book");
    MyBook.Summarize();
    FixedBookCollection<FairyTale> FBooks("Fairytale Books");
    FairyTale MyTale("Tale", "Joe");
    FBooks += MyTale;
    BookCollection E("E");
    MyLibrary += E;
    MyLibrary += FBooks;
    MyLibrary.Summarize();
    MyLibrary -= FBooks;
    MyLibrary.Summarize();
    FixedSizeBookCollection<5> Collection("My Fixed Size Collection");
    /* Extension Work */ Book* Duplicate = MyLibrary.DuplicateBook(&MyBook);
    /* Extension Work */ Duplicate->Summarize();
END

principal.h

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <type_traits>
#include <array>
#ifndef __cplusplus
#error Not C++
#endif
#define START int main(void)try{
#define END GET_ENTER_EXIT return(0);}catch(const std::exception& e){if(ON_ERROR_CLEAR_SCREEN){system("cls");}std::cerr << "Error: " << e.what() << std::endl; GET_ENTER_EXIT return (1);}
#define GET_ENTER_EXIT std::cout << "Press enter to exit" << std::endl; getchar();
class Book;
class Library;
typedef std::vector<const Book*> Books;
bool sContains(const std::string s, const char c){
    return (s.find(c) != std::string::npos);
}
bool approve(std::string s){
    return (!sContains(s, '#') && !sContains(s, '%') && !sContains(s, '~'));
}
template <class C> bool isBook(){
    return (typeid(C) == typeid(Book) || std::is_base_of<Book, C>());
}
template<class ClassToDuplicate> class DuplicatableClass{ 
public:
    ClassToDuplicate* Duplicate(ClassToDuplicate ToDuplicate){
        return new ClassToDuplicate(ToDuplicate);
    }
};
class Book : private DuplicatableClass<Book>{
friend class Library;
friend struct BookCollection;
public:
    Book(const char* Name, const char* Author) : name_(Name), author_(Author){}
    void operator+=(const char* Page){
        pages_.push_back(Page);
    }
    void EditAuthor(const char* AuthorName){
        if(approve(AuthorName)){
            author_ = AuthorName;
        }
        else{
            std::ostringstream errorMessage;
            errorMessage << "The author of the book " << name_ << " could not be changed as it was not approved";
            throw std::exception(errorMessage.str().c_str());
        }
    }
    void EditName(const char* Name){
        if(approve(Name)){
            name_ = Name;
        }
        else{
            std::ostringstream errorMessage;
            errorMessage << "The name of the book " << name_ << " could not be changed as it was not approved";
            throw std::exception(errorMessage.str().c_str());
        }
    }
    virtual void Summarize(){
        std::cout << "Book called " << name_ << "; written by " << author_ << ". Contains "
            << pages_.size() << ((pages_.size() == 1) ? " page:" : ((pages_.size() > 0) ? " pages:" : " pages")) << std::endl;
        if(pages_.size() > 0){
            ListPages(std::cout);
        }
    }
private:
    std::vector<const char*> pages_;
    const char* name_;
    const char* author_;
    void ListPages(std::ostream& output){
        for(int i = 0; i < pages_.size(); ++i){
            output << pages_[i] << std::endl;
        }
    }
};
class FairyTale : public Book{
public:
    FairyTale(const char* Name, const char* Author) : Book(Name, Author){}
};
struct BookCollection{
friend class Library;
    BookCollection(const char* Name) : name_(Name){}
    virtual void operator+=(const Book& Book)try{
        Collection.push_back(&Book); 
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
    virtual void operator-=(const Book& Book){
        for(int i = 0; i < Collection.size(); ++i){
            if(Collection[i] == &Book){
                Collection.erase(Collection.begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
private:
    const char* name_;
    Books Collection;
};
template<class FixedType> struct FixedBookCollection : public BookCollection{
    FixedBookCollection(const char* Name) : BookCollection(Name){
        if(!isBook<FixedType>()){
            std::ostringstream errorMessage;
            errorMessage << "The type " << typeid(FixedType).name() << " cannot be initialized as a FixedBookCollection";
            throw std::exception(errorMessage.str().c_str());
            delete this;
        }
    }
    void operator+=(const FixedType& Book)try{
        Collection.push_back(&Book); 
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
    void operator-=(const FixedType& Book){
        for(int i = 0; i < Collection.size(); ++i){
            if(Collection[i] == &Book){
                Collection.erase(Collection.begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
private:
    std::vector<const FixedType*> Collection;
};
template<size_t Size> struct FixedSizeBookCollection : private std::array<const Book*, Size>{
    FixedSizeBookCollection(const char* Name) : name_(Name){ if(Size < 1){ throw std::exception("A fixed size book collection cannot be smaller than 1"); currentPos = 0; } }
    void operator+=(const Book& Book)try{
        if(currentPos + 1 > Size){
            std::ostringstream errorMessage;
            errorMessage << "The FixedSizeBookCollection " << name_ << "'s size capacity has been overfilled";
            throw std::exception(errorMessage.str().c_str());
        }
        this->at(currentPos++) = &Book;
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
private:
    const char* name_;
    int currentPos;
};
class Library : private std::vector<const BookCollection*>{
public:
    void operator+=(const BookCollection& Collection){
        for(int i = 0; i < size(); ++i){
            if((*this)[i] == &Collection){
                std::ostringstream errorMessage;
                errorMessage << "The BookCollection " << Collection.name_ << " was already in the library, and therefore cannot be added";
                throw std::exception(errorMessage.str().c_str());
            }
        }
        push_back(&Collection);
    }
    void operator-=(const BookCollection& Collection){
        for(int i = 0; i < size(); ++i){
            if((*this)[i] == &Collection){
                erase(begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The BookCollection " << Collection.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
    Book* DuplicateBook(Book* Book)const{
        return (Book->Duplicate(*Book));
    }
    void Summarize(){
        std::cout << "Library, containing " << size() << ((size() == 1) ? " book collection:" : ((size() > 0) ? " book collections:" : " book collections")) << std::endl;
        if(size() > 0){
            for(int i = 0; i < size(); ++i){
                std::cout << (*this)[i]->name_ << std::endl;
            }
        }
    }
};

1voto

user2976089 Points 163

Voici un exemple de base pour les classes polymorphes

 #include <iostream>

class Animal{
public:
   Animal(const char* Name) : name_(Name){/* Add any method you would like to perform here*/
    virtual void Speak(){
        std::cout << "I am an animal called " << name_ << std::endl;
    }
    const char* name_;
};

class Dog : public Animal{
public:
    Dog(const char* Name) : Animal(Name) {/*...*/}
    void Speak(){
        std::cout << "I am a dog called " << name_ << std::endl;
    }
};

int main(void){
    Animal Bob("Bob");
    Dog Steve("Steve");
    Bob.Speak();
    Steve.Speak();
    //return (0);
}
 

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