Funções de membro vs. Funções de não membro para operadores matemáticos

12

Estou escrevendo uma biblioteca de álgebra linear (para encurtar a história, é uma tarefa da escola) que envolve matrizes, vetores etc. No processo de criação dessa biblioteca, vou criar funções que executam operações matemáticas em objetos. Por exemplo, transponha matriz, matriz invertida, normalize vetor etc.

Fiquei curioso para saber qual é a "melhor prática" para esse tipo de função ... Ou seja, devo fazer da função uma função membro ou não membro? (Para maior clareza / uso da biblioteca)

Exemplo:

//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 algum padrão em relação a esse tipo de operação? Ou, pelo menos, existe uma maneira comum de as pessoas fazerem isso? Estou inclinado para a primeira opção, mas gostaria de saber se isso é recomendado.

apnorton
fonte

Respostas:

7

Isso é apenas uma questão de estilo e bom gosto. Eu já vi diferentes bibliotecas de álgebra linear,

  • escrito em estilo OOP usando funções-membro

  • escrito em um estilo não OOP usando apenas funções livres

  • fornecendo uma API com ambos

e todos eles estavam trabalhando. Pelo menos, sua API deve ser consistente; portanto, escolha sua opção e certifique-se de não combinar esses estilos arbitrariamente.

Doc Brown
fonte
8

Evite as taxas de associação: sempre que possível, prefira tornar as funções não-membros não-amigos.

Funções de não-membros que não são membros aprimoram o encapsulamento minimizando dependências: O corpo da função não pode depender dos membros não-públicos da classe (consulte o Item 11). Eles também separam classes monolíticas para liberar a funcionalidade separável, reduzindo ainda mais o acoplamento (consulte o Item 33). Eles melhoram a genicidade, porque é difícil escrever modelos que não sabem se uma operação é ou não um membro para um determinado tipo [...]

Herb Sutter


fonte
1
Finalmente, uma boa resposta: Tarde, mas não muito tarde. ;-) Outra referência: GotW # 84: Monólitos "Unstrung"
Deduplicator
1

As funções de membro e de não membro têm vantagens reais além do mero gosto. Por exemplo, o (s) array (s) subjacente (s) usado (s) para implementar uma matrixclasse provavelmente será private, portanto, você precisará usá- friendlo, a menos que use funções-membro. Nesse aspecto, um design de OO pode ser melhor.

No entanto, lembre-se de que, de tempos em tempos, os cientistas da computação precisam implementar uma fórmula matemática complexa sem sempre saber o que realmente faz. Quando preciso fazer isso, prefiro funções que não sejam membros, pois o resultado "visual" estará mais próximo da fórmula matemática original (uma vez que a fórmula matemática geralmente usa um estilo funcional).

No final, a resposta é a mesma do doutor Brown: é uma questão de gosto. Em resumo, você pode considerar escrever uma biblioteca no estilo OO com funções-membro (mais fácil de escrever, não friend) e depois escrever funções não-membros para executar as mesmas tarefas que simplesmente encaminham seus argumentos para os membros. Ele permite que você seja consistente e permita que o usuário escolha o que acha melhor.

Morwenn
fonte
0

Se você se aprofundar na declaração do problema, é claro que em algum momento você usará estruturas de dados personalizadas que representam certas entidades matemáticas. Por exemplo, você pode representar uma matriz meus meios de uma matriz n-dimensional. Para elaborar seu exemplo,

// 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();

(O exemplo de C ++ é simplificado, você pode usar modelos e coisas assim.)

O ponto a considerar é a facilidade de usar estruturas de dados personalizadas nos dois casos. O C ++ (ou qualquer outro objeto orientado) como uma linguagem é mais poderoso por si só, e isso se estende ao consumidor da biblioteca tanto quanto beneficia o implementador. Eu usei bibliotecas apenas C no passado e essas estruturas de dados personalizadas parecem chegar a todo o lugar em pouco tempo! Considerando que a interoperabilidade é mais fácil de alcançar com o último (usando construtores especiais, talvez?)

Para encerrar, se você estiver usando C ++, uma abordagem orientada a objetos é definitivamente recomendada.

dotbugfix
fonte
5
Desculpe, há muito pouco fato sólido em seu raciocínio. Não há diferença na facilidade de uso entre seus dois transposeexemplos. O principal motivo pelo qual o C ++ é mais fácil, é porque a semântica de cópia e atribuição realmente funciona.A = Bpode compilar em C, mas provavelmente faz a coisa errada.
MSalters 25/02
+1 para semântica de cópia O ponto que eu estava tentando entender é usar objetos (encapsulações sobre "representações") v / s usando um conjunto de estruturas e funções relacionadas fracamente acopladas.
dotbugfix