Override et surcharge en C++

Oui, je comprends la différence entre eux. Ce que je veux savoir est: pourquoi outrepasser une méthode? Qu'est-ce que le bon de le faire? En cas de surcharge: le seul avantage est que vous n'avez pas à penser dans des noms différents à des fonctions?

29
demandé sur Daniel Daranas 2009-01-09 21:22:45

7 réponses

Surcharge signifie généralement que vous avez deux ou plusieurs fonctions dans la même portée ayant le même nom. La fonction qui correspond le mieux aux arguments lorsqu'un appel est fait gagne et est appelée. Il est Important de noter, au lieu d'appeler une fonction virtuelle, que la fonction appelée est sélectionnée au moment de la compilation. Tout dépend du type statique de l'argument. Si vous avez une surcharge de B et un D, et l'argument est une référence à B, mais c'est vraiment des points à un D objet, puis la surcharge pour B est choisi en C++. Cela s'appelle répartition statique plutôt pour Dynamic dispatch. Vous surcharge si vous voulez faire la même chose qu'une autre fonction ayant le même nom, mais vous voulez le faire pour un autre type d'argument. Exemple:

void print(Foo const& f) {
    // print a foo
}

void print(Bar const& bar) {
    // print a bar
}

ils impriment tous les deux leur argument, donc ils sont surchargés. Mais le premier imprime un foo, et le second imprime une barre. Si vous avez deux fonctions qui font des choses différentes, il est considéré comme de mauvais style quand ils ont le même nom, parce que cela peut conduire à la confusion sur ce qui se passera réellement lors de l'appel des fonctions. Une autre usecase pour la surcharge est lorsque vous avez des paramètres supplémentaires pour les fonctions, mais ils ne font que transférer le contrôle vers d'autres fonctions:

void print(Foo & f, PrintAttributes b) { 
    /* ... */ 
}

void print(Foo & f, std::string const& header, bool printBold) {
    print(f, PrintAttributes(header, printBold));
}

Qui peut être pratique pour l'appelant, si les options les surcharges sont souvent utilisés.

Prépondérant est quelque chose de complètement différent. Il ne rivalise pas avec la surcharge. Cela signifie que si vous avez une fonction virtuelle dans une classe de base, vous pouvez écrire une fonction avec la même signature dans la classe dérivée. La fonction dans la classe dérivée remplace la fonction de la base. Exemple:

struct base {
    virtual void print() { cout << "base!"; }
}

struct derived: base {
    virtual void print() { cout << "derived!"; }
}

maintenant, si vous avez un objet et appelez le print fonction de membre, la fonction d'impression de la dérivée est toujours appelée, parce qu'elle l'emporte sur celle de la base. Si l' la fonction print n'était pas virtuel, alors la fonction dans le dérivé ne supplanterait pas la fonction de base, mais simplement masquer. La suppression peut être utile si vous avez une fonction qui accepte une classe de base, et toutes celles qui en sont dérivées:

void doit(base &b) {
    // and sometimes, we want to print it
    b.print();
}

maintenant, même si au moment de la compilation le compilateur sait seulement que b est au moins base, l'impression de la classe dérivée sera appelée. C'est le point de fonctions virtuelles. Sans eux, la fonction d'impression de la base serait appelé, et celui de la classe dérivée ne l'outrepasserait pas.

64
répondu Johannes Schaub - litb 2009-01-10 14:53:20

ceci ajoutera un peu plus de clarté aux pensées.enter image description here

27
répondu subhash kumar singh 2014-06-10 18:55:24

Vouscharger fonctions pour trois raisons:

  1. fournir deux (ou plus) fonctions qui exécutent des choses similaires, étroitement liées, différenciées par les types et/ou le nombre d'arguments qu'il accepte. Exemple artificiel:

    void Log(std::string msg); // logs a message to standard out
    void Log(std::string msg, std::ofstream); // logs a message to a file
    
  2. fournir deux (ou plus) des moyens pour effectuer la même action. Exemple artificiel:

    void Plot(Point pt); // plots a point at (pt.x, pt.y)
    void Plot(int x, int y); // plots a point at (x, y)
    
  3. pour fournir la capacité d'effectuer une action équivalente donnée deux (ou plus) différents types d'entrées. Exemple artificiel:

    wchar_t      ToUnicode(char c);
    std::wstring ToUnicode(std::string s);
    

cas, il vaut la peine de faire valoir qu'une fonction d'un nom différent est un meilleur choix qu'une fonction surchargée. Dans le cas des constructeurs, la surcharge est le seul choix.


cheval une fonction est entièrement différente, et sert un but entièrement différent. La fonction primordiale est de savoir comment le polymorphisme fonctionne en C++. Vous remplacer une fonction pour modifier le comportement de cette fonction dans une classe dérivée. De cette façon, une classe de base fournit une interface, et la classe dérivée fournit une implémentation.

18
répondu P Daddy 2011-03-25 14:09:51

Override est utile lorsque vous héritez d'une classe de base et souhaitez étendre ou modifier ses fonctionnalités. Même lorsque l'objet est lancé comme classe de base, il appelle votre fonction outrepassée, pas celle de base.

la surcharge n'est pas nécessaire, mais elle rend la vie plus facile ou plus lisible parfois. Sans doute il peut faire pire, mais il ne doit pas être utilisé. Par exemple, vous pouvez avoir deux fonctions qui effectuent la même opération, mais d'agir sur différents types de choses. Par exemple,Divide(float, float) devrait être différent de Divide(int, int), mais ils sont fondamentalement la même opération. Ne préféreriez-vous pas vous souvenir d'un nom de méthode, "diviser", plutôt que de devoir vous souvenir de "Diveefloat", "Diveeintbyfloat", et ainsi de suite?

4
répondu lc. 2009-01-09 18:26:10

les gens ont déjà défini à la fois la surcharge et le dépassement, donc je ne vais pas développer.

ASAFE demande:

le seul avantage [à surcharger] est que vous n'avez pas pensé à plusieurs noms de fonctions?

1. Vous n'avez pas à penser à plusieurs noms de

Et c'est déjà un puissant avantage, n'est-ce pas?

comparons avec les fonctions connues de L'API C, et leurs variantes fictives de C++:

/* C */
double fabs(double d) ;
int abs(int i) ;

// C++ fictional variants
long double abs(long double d) ;
double abs(double d) ;
float abs(float f) ;
long abs(long i) ;
int abs(int i) ;

Cela signifie deux choses: d'abord, vous devez indiquer au compilateur le type de données, il se nourrit de la fonction en choisissant la bonne fonction. Deuxièmement, si vous voulez l'étendre, vous aurez besoin de trouver des noms fantaisistes, et l'utilisateur de vos fonctions devra se souvenir des bons noms fantaisistes.

Et tout ce qu'il/Elle voulait avoir la valeur absolue de certaines numérique variable...

Une action, une et une seule fonction nom.

Notez que vous n'êtes pas limité à changer le type d'un paramètre. Tout peut changer, tant que c'est logique.

2. Pour les opérateurs, il est obligatoire

voyons les opérateurs:

// C++
Integer operator + (const Integer & lhs, const Integer & rhs) ;
Real operator + (const Real & lhs, const Real & rhs) ;
Matrix operator + (const Matrix & lhs, const Matrix & rhs) ;
Complex operator + (const Complex & lhs, const Complex & rhs) ;

void doSomething()
{
   Integer i0 = 5, i1 = 10 ;
   Integer i2 = i0 + i1 ; // i2 == 15

   Real r0 = 5.5, r1 = 10.3 ;
   Real r2 = r0 + r1 ; // r2 = 15.8

   Matrix m0(1, 2, 3, 4), m1(10, 20, 30, 40) ;
   Matrix m2 = m0 + m1 ; // m2 == (11, 22, 33, 44)

   Complex c0(1, 5), c1(10, 50) ;
   Complex c2 = c0 + c1 ; // c2 == (11, 55)
}

Dans l'exemple ci-dessus, vous voulez pour éviter d'utiliser autre chose que de l'opérateur+.

notez que C a une surcharge implicite de l'opérateur pour les types intégrés (y compris C99 complexe type):

/* C */
void doSomething(void)
{
   char c = 32 ;
   short s = 54 ;
   c + s ; /* == C++ operator + (char, short) */
   c + c ; /* == C++ operator + (char, char) */
}

Donc, même dans les langages objets, cette surcharge chose est utilisée.

3. Pour les objets, il est obligatoire

voyons l'utilisation d'un objet méthodes de base: ses constructeurs:

class MyString
{
   public :
      MyString(char character) ;
      MyString(int number) ;
      MyString(const char * c_style_string) ;
      MyString(const MyString * mySring) ;
      // etc.
} ;

certains pourraient considérer cela comme une surcharge de la fonction, mais en fait, c'est plus similaire à la surcharge de l'opérateur:

void doSomething()
{
   MyString a('h') ;                  // a == "h" ;
   MyString b(25) ;                   // b == "25" ;
   MyString c("Hello World") ;        // c == "Hello World" ;
   MyString d(c) ;                    // d == "Hello World" ;
}

Conclusion: la Surcharge est cool

en C, lorsque vous donnez le nom de la fonction, le les paramètres sont implicitement partie de la signature à l'appel. Si vous avez "double fabs(double d)", Alors alors que la signature de fabs pour le compilateur est "fabs" non décorée, cela signifie que vous doit savoir qu'il suffit de doubles.

en C++, le nom de la fonction ne signifie pas que sa signature est forcée. Sa signature à l'appel est son nom et ses paramètres. Ainsi, si vous écrivez abs (-24), le compilateur saura quelle surcharge de abs il doit appeler, et vous, en écrivant il, le trouver plus naturel: vous voulez la valeur absolue de -24.

en tout cas, toute personne qui a codé un peu dans n'importe quelle langue avec des opérateurs utilise déjà la surcharge, qu'il s'agisse D'opérateurs numériques de base, de concaténation Java string, de délégués C#, etc.. Pourquoi? parce que c'est plus naturel.

et les exemples ci-dessus ne sont que la pointe de l'iceberg: en utilisant des gabarits, la surcharge devient très utile, mais c'est une autre histoire.

3
répondu paercebal 2018-02-21 04:26:15

l'exemple de manuel est Animal de classe avec la méthode speak (). La sous-classe de chien remplace speak() par" bark "tandis que la sous-classe de chat remplace speak () par"meow".

2
répondu jdigital 2009-01-09 18:32:13

utilisation de la surcharge est pour une utilisation dans les modèles. Dans les modèles, vous écrivez du code qui peut être utilisé sur différents types de données, et l'appelez avec différents types. Si les fonctions qui prennent des arguments différents devaient être nommées différemment, le code pour les différents types de données devrait en général être différent, et les modèles juste ne fonctionneraient pas.

même si vous n'êtes pas encore en train d'écrire des modèles, vous en utilisez certainement certains. Les flux sont des modèles, tout comme les vecteurs. Sans surcharge, et donc sans gabarits, vous auriez besoin d'appeler des flux Unicode quelque chose de différent des flux ASCII, et vous auriez à utiliser des tableaux et des pointeurs au lieu de vecteurs.

1
répondu David Thornley 2009-01-09 20:15:16