Fonctions membres et fonctions non membres pour les opérateurs mathématiques

12

J'écris une bibliothèque d'algèbre linéaire (histoire courte, c'est un devoir d'école) qui implique des matrices, des vecteurs, etc. Dans le processus de création de cette bibliothèque, je vais créer des fonctions qui effectuent des opérations mathématiques sur des objets. Par exemple, transposer la matrice, inverser la matrice, normaliser le vecteur, etc.

J'étais curieux de savoir quelle était la "meilleure pratique" pour ce type de fonction ... Autrement dit, devrais-je faire de la fonction une fonction membre ou non membre? (Par souci de clarté / utilisation de la bibliothèque)

Exemple:

//Member function way:
B = A.transpose();
C = A.inverse();

//Non-member function way:
B = linalg::transpose(A); //Non-member transpose function in linear algebra namespace
C = linalg::inverse(A);

Existe-t-il une norme concernant ce type d'opérations? Ou, au moins, y a-t-il une façon courante pour les gens de faire cela? Je penche vers la première option, mais j'aimerais savoir si cela est recommandé.

apnorton
la source

Réponses:

7

C'est juste une question de style et de goût. J'ai vu différentes bibliothèques d'algèbre linéaire, soit

  • écrit dans un style POO à l'aide des fonctions membres

  • écrit dans un style non-OOP en utilisant uniquement des fonctions libres

  • fournir une API avec les deux

et tous travaillaient. Au moins, votre API doit être cohérente, alors choisissez votre choix et assurez-vous de ne pas mélanger ces styles arbitrairement.

Doc Brown
la source
8

Évitez les frais d'adhésion: dans la mesure du possible, préférez rendre les fonctions non membres non amis.

Les fonctions non amis non membres améliorent l'encapsulation en minimisant les dépendances: le corps de la fonction ne peut pas dépendre des membres non publics de la classe (voir le point 11). Ils séparent également les classes monolithiques pour libérer la fonctionnalité séparable, réduisant encore le couplage (voir point 33). Ils améliorent la généricité, car il est difficile d'écrire des modèles qui ne savent pas si une opération est membre d'un type donné [...]

Herb Sutter


la source
1
Enfin une bonne réponse: tard, mais pas trop tard. ;-) Une autre référence: GotW # 84: Monoliths "Unstrung"
Deduplicator
1

Les fonctions membres et non membres ont de réels avantages en plus du simple goût. Par exemple, les tableaux sous-jacents utilisés pour implémenter une matrixclasse seront probablement private, par conséquent, vous devrez utiliser à friendmoins que vous n'utilisiez des fonctions membres. À cet égard, une conception OO peut être meilleure.

Cependant, gardez à l'esprit que, de temps en temps, les informaticiens doivent mettre en œuvre une formule mathématique complexe sans toujours savoir ce que vous faites réellement. Quand je dois le faire, je préfère les fonctions non membres car le résultat "visuel" sera plus proche de la formule mathématique d'origine (puisque la formule mathématique utilise généralement un style fonctionnel).

Au final, la réponse est la même que celle de Doc Brown: c'est une question de goût. En bref, vous pourriez envisager d'écrire une bibliothèque de style OO avec des fonctions membres (plus facile à écrire, non friend), puis d'écrire des fonctions non membres pour effectuer les mêmes tâches qui transmettront simplement leurs arguments aux membres. Il vous permet d'être cohérent et de laisser l'utilisateur choisir ce qu'il pense être le mieux.

Morwenn
la source
0

Si vous approfondissez votre énoncé de problème, il est clair qu'à un moment donné, vous utiliserez des structures de données personnalisées qui représentent certaines entités mathématiques. Par exemple, vous pourriez représenter une matrice mes moyens d'un tableau à n dimensions. Pour développer votre exemple,

// C way
typedef struct {
    int data[MAX_LEN][MAX_LEN];
} MATRIX;

MATRIX B = transpose(A);

// C++ way
class Matrix; // Forward Declaration
class Matrix {
    int data[MAX_LEN][MAX_LEN];
    public:
        Matrix transpose();
};

Matrix B = A.transpose();

(L'exemple C ++ est simplifié, vous pouvez utiliser des modèles et autres.)

Le point à considérer est la facilité d'utilisation des structures de données personnalisées dans les deux cas. Le C ++ (ou tout objet orienté) en tant que langage est plus puissant en soi, et cela s'étend au consommateur de la bibliothèque autant qu'il profite à l'implémenteur. J'ai utilisé des bibliothèques C uniquement dans le passé et ces structures de données personnalisées semblent se répandre partout en un rien de temps! Alors que l'interopérabilité est plus facile à réaliser avec ces derniers (à l'aide de constructeurs spéciaux, peut-être?)

Pour finir, si vous utilisez C ++, une approche orientée objet est définitivement recommandée.

dotbugfix
la source
5
Désolé, il y a très peu de faits solides dans votre raisonnement. Il n'y a aucune différence de facilité d'utilisation entre vos deux transposeexemples. La raison principale pour laquelle C ++ est plus facile est que la sémantique de copie et d'affectation fonctionne réellement. A = Bpeut compiler en C mais fait probablement la mauvaise chose.
MSalters le
+1 pour la sémantique de copie Le point que j'essayais de faire passer est d'utiliser des objets (encapsulations sur des "représentations") v / s en utilisant un ensemble de structures faiblement couplées et des fonctions connexes.
dotbugfix