27 votes

En option paramètres de la fonction: Utiliser des arguments par défaut (NULL) ou surcharge de la fonction?

J'ai une fonction qui transforme un vecteur donné, mais peut aussi créer un tel vecteur d'elle-même si elle n'est pas donnée.

Je vois deux choix de conception pour un tel cas, si un paramètre de la fonction est facultative:

Faire un pointeur et d'en faire NULL par défaut:

void foo(int i, std::vector<int>* optional = NULL) {
  if(optional == NULL){
    optional = new std::vector<int>();
    // fill vector with data
  }
  // process vector
}

Ou ont deux fonctions avec une surcharge du nom, et qui laisse de côté l'argument:

void foo(int i) {
   std::vector<int> vec;
   // fill vec with data
   foo(i, vec);
}

void foo(int i, const std::vector<int>& optional) {
  // process vector
}

Sont là les raisons de préférer une solution plutôt que l'autre?

J'ai légèrement préfère la deuxième, parce que je peux faire le vecteur d'un const de référence, puisqu'il est, lorsqu'il est fourni, seulement lu, écrit, non. Aussi, l'interface semble plus propre (n'est-ce pas NULL juste un hack?). Et la différence de performance résultant de l'indirect appel de fonction est probablement optimisé loin.

Pourtant, je vois souvent la première solution dans le code. Existe-il des raisons impérieuses de le préférer, en dehors de programmeur de la paresse?

32voto

John Dibling Points 56814

Je ne voudrais pas utiliser l'une ou l'autre approche.

Dans ce contexte, le but de foo() semble être à un processus d'un vecteur. C'est, foo()'s de travail est de traiter le vecteur.

Mais dans la deuxième version de foo(), il est implicitement donné un deuxième emploi: pour créer le vecteur. La sémantique entre les foo() de la version 1 et foo() de la version 2 ne sont pas les mêmes.

Au lieu de cela, je considère avoir juste une foo() fonction pour traiter un vecteur, et une autre fonction qui crée le vecteur, si vous avez besoin d'une telle chose.

Par exemple:

void foo(int i, const std::vector<int>& optional) {
  // process vector
}

std::vector<int>* makeVector() {
   return new std::vector<int>;
}

Bien entendu, ces fonctions sont triviales, et si tous les makeVector() doit faire pour obtenir du travail fait est littéralement juste l'appeler de nouveau, alors il peut y avoir aucun point en ayant la makeVector() fonction. Mais je suis sûr que dans votre situation réelle, ces fonctions ne beaucoup plus que ce qui est montré ici, et mon code ci-dessus illustre une approche fondamentale et sémantique du design: donner une fonction, un travail à faire.

La conception que j'ai ci-dessus pour les foo() la fonction illustre également une autre approche fondamentale que personnellement, je utiliser dans mon code quand il s'agit de la conception d'interfaces -- qui comprend les signatures de fonction, classes, etc. C'est ceci: je crois qu' une bonne interface est 1) facile et intuitif à utiliser correctement, et 2) difficile ou impossible d'utiliser de manière incorrecte . Dans le cas de l'foo() fonction que nous implictly dire que, avec mon design, le vecteur est nécessaire de déjà exister et être "prêt". Par la conception de foo() pour prendre une référence au lieu d'un pointeur, c'est à la fois intuitive que l'appelant doit déjà disposer d'un vecteur, et ils vont avoir du mal à passer dans quelque chose qui n'est pas un prêt-à-go vecteur.

17voto

LeopardSkinPillBoxHat Points 11851

Je serais plutôt en faveur de la 2ème approche de méthodes surchargées.

La première approche (paramètres facultatifs) brouille la définition de la méthode car il n'a plus un seul but bien précis. Cela augmente la complexité du code, le rendant plus difficile pour quelqu'un qui n'y sont pas habitués à le comprendre.

Avec la deuxième approche (méthodes surchargées), chaque méthode a un objectif clair. Chaque méthode est bien structuré et cohérent. Quelques notes supplémentaires:

  • Si il y a du code qui doit être dupliqué dans les deux méthodes, ce qui peut être extrait dans une méthode distincte et chaque méthode surchargée pourrait appeler cette méthode externe.
  • Je voudrais aller plus loin et le nom de chaque méthode différemment pour indiquer les différences entre les méthodes. Cela permet de rendre le code plus d'auto-documentation.

15voto

David Ruhmann Points 5813

Alors que je ne comprendre les plaintes de beaucoup de gens en ce qui concerne les paramètres par défaut et les surcharges, il semble y avoir un manque de compréhension des avantages que ces caractéristiques fournissent.

Valeurs De Paramètre Par Défaut:
D'abord, je tiens à souligner que, lors de la conception initiale d'un projet, il devrait y avoir peu ou pas d'utilisation de valeurs par défaut si elle est bien conçue. Cependant, lorsqu'il y a défaut de " plus grand actifs entre en jeu est avec des projets existants et bien établi Api. Je travaille sur des projets qui consistent en des millions de lignes de code et n'ont pas le luxe de re-coder toutes. Ainsi, lorsque vous souhaitez ajouter une nouvelle fonctionnalité qui nécessite un paramètre supplémentaire; un par défaut est nécessaire pour que le nouveau paramètre. Sinon vous cassez tout le monde qui utilise votre projet. Ce qui serait bien avec moi personnellement, mais je doute de votre entreprise ou les utilisateurs de votre produit/API apprécierait d'avoir à re-coder leurs projets à chaque mise à jour. Simplement, les valeurs par Défaut sont grands pour la rétrocompatibilité! C'est généralement la raison pour laquelle vous allez voir les valeurs par défaut dans les grandes Api ou de projets existants.

La Fonction Remplace: L'avantage de la fonction remplace, c'est qu'ils permettent le partage d'une fonctionnalité concept, mais avec différentes options/paramètres. Cependant, de nombreuses fois, je vois de la fonction remplace paresseusement utilisé pour fournir radicalement différentes fonctionnalités, avec juste un peu différents paramètres. Dans ce cas, elles doivent avoir chacun séparément des fonctions nommées, relatives à leurs fonctionnalités spécifiques (Comme avec le cas des OP par exemple).

Ces, fonctionnalités de c/c++ sont bon et bien lorsqu'il est utilisé correctement. Qui peut être dit de plus tout de programmation. C'est quand ils sont maltraités/abusé qu'ils causent des problèmes.

Avertissement:
Je sais que cette question a déjà quelques années, mais depuis ces réponses est venu dans mes résultats de recherche aujourd'hui (2012), j'ai ressenti ce besoin d'examiner de manière plus approfondie pour les futurs lecteurs.

4voto

Sid Sarasvati Points 76

Un références ne peut pas être NULL en C++, une très bonne solution serait d'utiliser Nullable modèle. Cela vous permettrait de faire des choses est ref.isNull()

Ici, vous pouvez utiliser ceci:

template<class T>
class Nullable {
public:
    Nullable() {
        m_set = false;
    }
    explicit
    Nullable(T value) {
        m_value = value;
        m_set = true;
    }
    Nullable(const Nullable &src) {
        m_set = src.m_set;
        if(m_set)
            m_value = src.m_value;
    }
    Nullable & operator =(const Nullable &RHS) {
        m_set = RHS.m_set;
        if(m_set)
            m_value = RHS.m_value;
        return *this;
    }
    bool operator ==(const Nullable &RHS) const {
        if(!m_set && !RHS.m_set)
            return true;
        if(m_set != RHS.m_set)
            return false;
        return m_value == RHS.m_value;
    }
    bool operator !=(const Nullable &RHS) const {
        return !operator==(RHS);
    }

    bool GetSet() const {
        return m_set;
    }

    const T &GetValue() const {
        return m_value;
    }

    T GetValueDefault(const T &defaultValue) const {
        if(m_set)
            return m_value;
        return defaultValue;
    }
    void SetValue(const T &value) {
        m_value = value;
        m_set = true;
    }
    void Clear()
    {
        m_set = false;
    }

private:
    T m_value;
    bool m_set;
};

Maintenant vous pouvez avoir

void foo(int i, Nullable<AnyClass> &optional = Nullable<AnyClass>()) {
   //you can do 
   if(optional.isNull()) {

   }
}

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