Non capisco gli argomenti contro il sovraccarico dell'operatore [chiuso]


82

Ho appena letto uno degli articoli di Joel in cui dice:

In generale, devo ammettere che ho un po 'paura delle caratteristiche del linguaggio che nascondono le cose . Quando vedi il codice

i = j * 5;

... in C sai, almeno, che j viene moltiplicato per cinque e i risultati memorizzati in i.

Ma se vedi lo stesso frammento di codice in C ++, non sai nulla. Niente. L'unico modo per sapere cosa sta realmente accadendo in C ++ è scoprire quali tipi io e j sono, qualcosa che potrebbe essere dichiarato da qualche altra parte. Questo perché j potrebbe essere di un tipo operator*sovraccarico e fa qualcosa di terribilmente spiritoso quando provi a moltiplicarlo.

(Enfasi mia.) Hai paura delle caratteristiche del linguaggio che nascondono le cose? Come puoi averne paura? Nascondere le cose (noto anche come astrazione ) non è una delle idee chiave della programmazione orientata agli oggetti? Ogni volta che chiami un metodo a.foo(b), non hai idea di cosa potrebbe fare. Devi scoprire quali tipi ae quali bsono, qualcosa che potrebbe essere dichiarato da qualche altra parte. Quindi dovremmo eliminare la programmazione orientata agli oggetti, perché nasconde troppe cose al programmatore?

E in che modo è j * 5diverso j.multiply(5), che potresti dover scrivere in una lingua che non supporta il sovraccarico dell'operatore? Ancora una volta, dovresti scoprire il tipo di je sbirciare all'interno del multiplymetodo, perché ecco, jpotrebbe essere di un tipo che ha un multiplymetodo che fa qualcosa di terribilmente spiritoso.

"Muahaha, sono un programmatore malvagio che nomina un metodo multiply, ma quello che fa in realtà è totalmente oscuro e non intuitivo e non ha assolutamente nulla a che fare con il moltiplicare le cose." È uno scenario che dobbiamo prendere in considerazione quando si progetta un linguaggio di programmazione? Quindi dobbiamo abbandonare gli identificatori dai linguaggi di programmazione perché potrebbero essere fuorvianti!

Se vuoi sapere cosa fa un metodo, puoi dare un'occhiata alla documentazione o sbirciare all'interno dell'implementazione. Il sovraccarico dell'operatore è solo zucchero sintattico e non vedo affatto come cambi il gioco.

Per favore, illuminami.


20
+1: argomento ben scritto, ben argomentato, interessante e altamente discutibile. Un brillante esempio di una domanda p.se.
Allon Guralnek,

19
+1: Le persone ascoltano Joel Spolsky perché scrive bene ed è noto. Ma questo non lo rende giusto il 100% delle volte. Sono d'accordo con il tuo argomento. Se tutti seguissimo la logica di Joel qui, non arriveremmo mai da nessuna parte.
Nessuno il

5
Direi che i e j sono dichiarati localmente in modo da poter vedere rapidamente il loro tipo, oppure sono nomi di variabili sucky e dovrebbero essere rinominati in modo appropriato.
Cameron MacFarland,

5
+1, ma non dimenticare la parte migliore dell'articolo di Joel: dopo aver corso una maratona verso la risposta corretta, senza motivo apparente si ferma a 50 piedi di distanza. Il codice sbagliato non dovrebbe solo apparire sbagliato; non dovrebbe compilare.
Larry Coleman,

3
@Larry: puoi fare in modo che il codice sbagliato non si compili definendo le classi in modo appropriato, quindi nel suo esempio potresti avere SafeString e UnsafeString in C ++ o RowIndex e ColumnIndex, ma dovresti quindi utilizzare il sovraccarico dell'operatore per farli comportare in modo intuitivo.
David Thornley,

Risposte:


32

L'astrazione 'nasconde' il codice in modo da non doverti preoccupare dei meccanismi interni e spesso quindi non puoi cambiarli, ma l'intenzione non era di impedirti di guardarlo. Facciamo solo ipotesi sugli operatori e, come ha detto Joel, potrebbe essere ovunque. Avere una funzione di programmazione che richiede che tutti gli operatori sovraccarichi siano stabiliti in una posizione specifica può aiutare a trovarla, ma non sono sicuro che ne semplifichi l'utilizzo.

Non vedo fare * fare qualcosa che non assomigli molto alla moltiplicazione meglio di una funzione chiamata Get_Some_Data che cancella i dati.


13
+1 Per il bit "Non vedo". Le funzionalità linguistiche sono disponibili per l'uso, non per gli abusi.
Michael K,

5
Eppure abbiamo un <<operatore definito su flussi che non ha nulla a che fare con lo spostamento bit a bit, proprio nella libreria standard di C ++.
Malcolm,

l'operatore "spostamento bit a bit" viene chiamato solo per ragioni storiche. Quando applicato a tipi standard, fa uno spostamento bit a bit (nello stesso modo in cui l'operatore + aggiunge numeri insieme quando applicato a tipi numerici), tuttavia quando applicato a un tipo complesso, può fare ciò che gli piace, purché faccia senso per quel tipo.
gbjbaanb,

1
* è utilizzato anche per il dereferenziamento (come fatto da puntatori intelligenti e iteratori); non è chiaro dove mettere il confine tra sovraccarico buono e cattivo
martinkunev,

Non sarebbe da nessuna parte, sarebbe nella definizione del tipo di j.
Andy,

19

IMHO, funzionalità linguistiche come il sovraccarico dell'operatore danno più potenza al programmatore. E, come tutti sappiamo, da un grande potere derivano grandi responsabilità. Le caratteristiche che ti danno più potenza ti danno anche più modi di spararti nel piede e, ovviamente, dovrebbero essere usate con giudizio.

Ad esempio, ha perfettamente senso sovraccaricare +l' *operatore class Matrixo per o class Complex. Tutti sapranno immediatamente cosa significa. D'altra parte, per me il fatto che ciò +significhi concatenare le stringhe non è affatto ovvio, anche se Java lo fa come parte del linguaggio e STL lo fa per std::stringusare il sovraccarico dell'operatore.

Un altro buon esempio di quando il sovraccarico dell'operatore rende più chiaro il codice sono i puntatori intelligenti in C ++. Volete che i puntatori intelligenti si comportino il più possibile come puntatori regolari, quindi ha perfettamente senso sovraccaricare l'unario *e gli ->operatori.

In sostanza, il sovraccarico dell'operatore non è altro che un altro modo per nominare una funzione. E c'è una regola per le funzioni di denominazione: il nome deve essere descrittivo, rendendo immediatamente ovvio ciò che fa la funzione. La stessa regola esatta si applica al sovraccarico dell'operatore.


1
Le tue ultime due frasi arrivano al centro dell'obiezione al sovraccarico dell'operatore: il desiderio che tutto il codice sia immediatamente evidente.
Larry Coleman,

2
Non è ovvio cosa significhi M * N, dove M e N sono di tipo Matrix?
Dima,

2
@Fred: No. Esiste un tipo di moltiplicazione di matrici. Puoi moltiplicare una matrice mxn per una matrice nxk e ottenere una matrice mxk.
Dima,

1
@FredOverflow: Esistono diversi modi per moltiplicare un vettore tridimensionale, uno che ti dà uno scalare e uno che ti dà un altro vettore tridimensionale, e quindi un sovraccarico *per quelli può causare confusione. Probabilmente potresti usarlo operator*()per il prodotto punto e operator%()per il prodotto incrociato, ma non lo farei per una libreria di uso generale.
David Thornley,

2
@Martin Beckett: No. C ++ non è autorizzato a riordinare A-Bcome B-Aentrambi, e tutti gli operatori seguono quel modello. Anche se c'è sempre un'eccezione: quando il compilatore può provare che non importa, è permesso riorganizzare tutto.
Sjoerd,

9

In Haskell "+", "-", "*", "/" etc sono solo funzioni (infix).

Dovresti nominare una funzione di infissione "più" come in "4 più 2"? Perché no, se l'aggiunta è ciò che fa la tua funzione. Dovresti nominare la tua funzione "più" "+"? Perchè no.

Penso che il problema con i cosiddetti "operatori" sia che somigliano per lo più a operazioni matematiche e non ci sono molti modi per interpretarle e quindi ci sono grandi aspettative su cosa faccia un tale metodo / funzione / operatore.

EDIT: chiarito il mio punto


Ehm, ad eccezione di ciò che è ereditato da C, C ++ (ed è quello che stava chiedendo Fred) fa praticamente la stessa cosa. Cosa ne pensi se questo è buono o cattivo?
sabato

@sbi Amo l'overloading degli operatori ... operatori in realtà anche C è sovraccarico ... È possibile usarli per int, float, long longe quant'altro. Quindi di cosa si tratta?
FUZxxl,

@FUZxxl: si tratta di operatori definiti dall'utente che sovraccaricano quelli integrati.
sabato

1
@sbi Haskell non fa distinzione tra builtin e definito dall'utente . Tutti gli operatori sono uguali. Puoi anche attivare alcune estensioni che rimuovono tutte le cose predefinite e ti permettono di scrivere qualsiasi cosa da zero, inclusi eventuali operatori.
FUZxxl,

@FUZxxl: Questo potrebbe benissimo essere, ma quelli che si oppongono agli operatori sovraccarichi di solito non si oppongono all'uso di built-in +per diversi tipi di numeri integrati, ma creando sovraccarichi definiti dall'utente. da qui il mio commento.
sbi,

7

Sulla base delle altre risposte che ho visto, posso solo concludere che la vera obiezione al sovraccarico dell'operatore è il desiderio di un codice immediatamente evidente.

Questo è tragico per due motivi:

  1. Portato alla sua logica conclusione, il principio secondo cui il codice dovrebbe essere immediatamente ovvio vorrebbe che tutti noi ancora stessimo codificando in COBOL.
  2. Non impari dal codice che è immediatamente ovvio. Impara dal codice che ha senso una volta che ti prendi del tempo per pensare a come funziona.

Imparare dal codice non è sempre l'obiettivo principale. In un caso del tipo "La caratteristica X è glitch, la persona che l'ha scritta ha lasciato la società, e devi ripararla al più presto", preferirei avere un codice immediatamente evidente.
Erroratz

5

Sono abbastanza d'accordo.

Se scrivi multiply(j,5), jpotrebbe essere di tipo scalare o matrice, rendendo multiply()più o meno complesso, a seconda di cosa jsia. Tuttavia, se abbandoni del tutto il sovraccarico, allora la funzione dovrebbe essere denominata multiply_scalar()o multiply_matrix()che renderebbe ovvio ciò che sta accadendo sotto.

C'è un codice in cui molti di noi lo preferiscono in un modo e c'è un codice in cui la maggior parte di noi lo preferisce in un altro modo. Gran parte del codice, tuttavia, cade nella terra di mezzo tra questi due estremi. Quello che preferisci lì dipende dal tuo background e dalle tue preferenze personali.


Buon punto. Tuttavia, abbandonare del tutto il sovraccarico non funziona bene con la programmazione generica ...
Fredoverflow il

@FredO: Certo che no. Ma la programmazione generica si basa sull'uso dello stesso algoritmo per tipi molto diversi, quindi anche quelli che preferiscono multiply_matrix()non apprezzeranno la programmazione generica.
sabato

2
Sei piuttosto ottimista riguardo ai nomi, vero? Sulla base di alcuni posti in cui ho lavorato, mi aspetterei nomi come 'multiply () `e' multiplym ()` o forse real_multiply()circa. Gli sviluppatori spesso non sono bravi con i nomi e operator*()almeno saranno coerenti.
David Thornley,

@ David: Sì, ho ignorato il fatto che i nomi potrebbero essere cattivi. Ma allora potremmo anche supporre che operator*()potrebbe fare qualcosa di stupido, jè una macro che valuta un'espressione che coinvolge cinque chiamate di funzione e quant'altro. Allora non puoi più confrontare i due approcci. Ma sì, nominare bene le cose è difficile, anche se vale la pena qualunque tempo ci voglia.
sabato

5
@ David: E dato che nominare le cose è difficile, i nomi dovrebbero essere banditi dai linguaggi di programmazione, giusto? È troppo facile sbagliarli! ;-)
fredoverflow l'

4

Vedo due problemi con il sovraccarico dell'operatore.

  1. Il sovraccarico modifica la semantica dell'operatore, anche se ciò non è previsto dal programmatore. Ad esempio, quando si sovraccarico &&, ||o ,, si perdono i punti di sequenza che sono implicite le varianti integrate di questi operatori (così come il comportamento corto circuito degli operatori logici). Per questo motivo, è meglio non sovraccaricare questi operatori, anche se la lingua lo consente.
  2. Alcune persone vedono l'operatore sovraccarico come una caratteristica così piacevole, iniziano a usarlo ovunque, anche se non è la soluzione appropriata. Questo fa sì che altre persone reagiscano in modo eccessivo nell'altra direzione e mettano in guardia contro il sovraccarico dell'operatore. Non sono d'accordo con nessuno dei due gruppi, ma prendo la via di mezzo: il sovraccarico dell'operatore dovrebbe essere usato con parsimonia e solo quando
    • l'operatore sovraccarico ha il significato naturale sia per gli esperti di dominio che per gli esperti di software. Se questi due gruppi non concordano sul significato naturale per l'operatore, non sovraccaricarlo.
    • per i tipi coinvolti, non esiste un significato naturale per l'operatore e il contesto immediato (preferibilmente la stessa espressione, ma non più di poche righe) chiarisce sempre qual è il significato dell'operatore. Un esempio di questa categoria sarebbe operator<<per i flussi.

1
+1 da me, ma il secondo argomento può ugualmente essere applicato all'eredità. Molte persone non hanno la minima idea dell'ereditarietà e provano ad applicarlo a tutto. Penso che la maggior parte dei programmatori concorderebbe sul fatto che è possibile abusare dell'eredità. Ciò significa che l'eredità è "malvagia" e dovrebbe essere abbandonata dai linguaggi di programmazione? O dovremmo lasciarlo perché può anche essere utile?
Fredoverflow,

@FredOverflow Il secondo argomento può essere applicato a tutto ciò che è "nuovo e caldo". Non lo sto dando come argomento per rimuovere il sovraccarico dell'operatore da una lingua, ma come motivo per cui le persone discutono contro di esso. Per quanto mi riguarda, il sovraccarico dell'operatore è utile ma dovrebbe essere usato con cura.
Bart van Ingen Schenau,

IMHO, consentire sovraccarichi &&e ||in un modo che non implica il sequenziamento è stato un grosso errore (IMHO, se C ++ avrebbe consentito il sovraccarico di quelli, avrebbe dovuto usare uno speciale formato "a due funzioni", con la prima funzione richiesta per restituire un tipo che era implicitamente convertibile in un numero intero; la seconda funzione poteva accettare due o tre argomenti, con l'argomento "extra" della seconda funzione come tipo di ritorno del primo. Il compilatore chiamava la prima funzione e quindi, se ha restituito un valore diverso da zero, valuta il secondo operando e chiama la seconda funzione su di esso.)
supercat

Ovviamente, non è così bizzarro come consentire l'overload dell'operatore virgola. Per inciso, una cosa di sovraccarico che non ho davvero visto, ma che vorrei, sarebbe un mezzo di accesso stretto ai membri, permettendo a un'espressione simile foo.bar[3].Xdi essere gestita dalla fooclasse, piuttosto che richiedere foodi esporre un membro che potrebbe supportare la sottoscrizione e quindi esporre un membro X. Se si volesse forzare la valutazione tramite l'accesso effettivo dei membri, si scriverebbe ((foo.bar)[3]).X.
supercat,

3

Sulla base della mia esperienza personale, il modo Java di consentire più metodi, ma senza sovraccaricare l'operatore, significa che ogni volta che vedi un operatore sai esattamente cosa fa.

Non devi vedere se *invoca uno strano codice ma sai che è un moltiplicatore e si comporta esattamente come nel modo definito dalla specifica del linguaggio Java. Ciò significa che puoi concentrarti sul comportamento reale invece di scoprire tutte le cose di wicket definite dal programmatore.

In altre parole, proibire il sovraccarico dell'operatore è un vantaggio per il lettore , non per lo scrittore , e quindi facilita la manutenzione dei programmi!


+1, con un avvertimento: C ++ ti dà abbastanza corda per impiccarti. Ma se voglio implementare un elenco collegato in C ++, vorrei poter usare [] per accedere all'ennesimo elemento. Ha senso usare gli operatori per i dati per cui (matematicamente parlando) sono validi.
Michael K,

@Michael, non riesci a convivere con la list.get(n)sintassi?

@ Thorbjørn: anche in realtà va bene, forse un cattivo esempio. Il tempo potrebbe essere migliore - sovraccaricare +, - avrebbe senso piuttosto che time.add (anotherTime).
Michael K,

4
@Michael: A proposito di elenchi collegati, std::listnon sovraccarica operator[](o fornisce qualsiasi altro mezzo di indicizzazione nell'elenco), poiché tale operazione sarebbe O (n) e un'interfaccia elenco non dovrebbe esporre tale funzione se ti interessa l'efficienza. I clienti potrebbero essere tentati di scorrere su elenchi collegati con indici, rendendo inutilmente gli algoritmi O (n) O (n ^ 2). Lo vedi abbastanza spesso nel codice Java, specialmente se le persone lavorano con l' Listinterfaccia che mira a sottrarre completamente la complessità.
Fredoverflow,

5
@Thor: "Ma per essere certi devi controllare :)" ... Ancora una volta, questo non è legato al sovraccarico dell'operatore . Se vedi time.add(anotherTime), dovrai anche verificare se il programmatore della libreria ha implementato l'operazione di aggiunta "correttamente" (qualunque cosa ciò significhi).
Fredoverflow il

3

Una differenza tra sovraccarico a * be chiamata multiply(a,b)è che quest'ultimo può essere facilmente superato. Se la multiplyfunzione non è sovraccaricata per tipi diversi, puoi scoprire esattamente cosa farà la funzione, senza dover tracciare i tipi di ae b.

Linus Torvalds ha un'interessante discussione sul sovraccarico dell'operatore. In qualcosa come lo sviluppo del kernel Linux, in cui la maggior parte delle modifiche vengono inviate tramite patch via e-mail, è importante che i manutentori possano capire cosa farà una patch con solo poche righe di contesto intorno a ogni modifica. Se le funzioni e gli operatori non sono sovraccarichi, la patch può essere più facilmente letta in modo indipendente dal contesto, poiché non è necessario passare attraverso il file modificato per capire quali sono tutti i tipi e verificare la presenza di operatori sovraccarichi.


Il kernel Linux non è sviluppato in C puro? Perché discutere del sovraccarico (dell'operatore) in questo contesto?
Fredoverflow,

Le preoccupazioni sono le stesse per qualsiasi progetto con un processo di sviluppo simile, indipendentemente dalla lingua. Un sovraccarico eccessivo può rendere difficile la comprensione dell'impatto delle modifiche se tutto ciò che devi fare sono poche righe da un file di patch.
Scott Wales,

@FredOverflow: il kernel Linux è davvero in GCC C. Utilizza ogni sorta di estensioni che danno al suo C un aspetto quasi C ++ in alcune occasioni. Sto pensando ad alcune delle manipolazioni del tipo di fantasia.
Zan Lynx,

2
@Scott: non ha senso discutere la "malvagità" del sovraccarico rispetto ai progetti programmati in C, perché C non ha la capacità di sovraccaricare le funzioni.
Fredoverflow,

3
Mi sembra che Linus Torvalds abbia uno stretto punto di vista. A volte critica cose che non sono veramente utili per la programmazione del kernel Linux come se ciò le rendesse inadatte per un uso generale. Subversion è un esempio. È un bel VCS, ma lo sviluppo del kernel Linux ha davvero bisogno di un VCS distribuito, quindi Linus ha criticato SVN in generale.
David Thornley,

2

Ho il sospetto che abbia qualcosa a che fare con la rottura delle aspettative. Sono abituato al C ++, sei abituato al comportamento dell'operatore che non è dettato interamente dalla lingua e non sarai sorpreso quando un operatore fa qualcosa di strano. Se sei abituato a linguaggi che non dispongono di tale funzionalità e poi vedi il codice C ++, porti con te le aspettative di quelle altre lingue e potresti essere brutalmente sorpreso quando scopri che un operatore sovraccarico fa qualcosa di strano.

Personalmente penso che ci sia una differenza. Quando è possibile modificare il comportamento della sintassi incorporata del linguaggio, diventa più opaco da ragionare. I linguaggi che non consentono la meta-programmazione sono sintatticamente meno potenti, ma concettualmente più semplici da comprendere.


Gli operatori sovraccaricati non dovrebbero mai fare "qualcosa di strano". Va bene se fa qualcosa di complesso, ovviamente. Ma sovraccarico solo quando ha un significato ovvio.
Sjoerd,

2

Penso che sovraccaricare gli operatori matematici non sia il vero problema con il sovraccarico degli operatori in C ++. Penso che gli operatori di sovraccarico che non dovrebbero fare affidamento sul contesto dell'espressione (cioè il tipo) siano "cattivi". Ad esempio sovraccarico , [ ] ( ) -> ->* new deleteo anche unario *. Hai un certo insieme di aspettative da quegli operatori che non dovrebbero mai cambiare.


+1 Non rendere [] l'equivalente di ++.
Michael K,

3
Stai dicendo che non dovremmo essere in grado di sovraccaricare gli operatori che hai menzionato affatto ? O stai solo dicendo che dovremmo sovraccaricarli solo per scopi sani di mente? Perché odierei vedere contenitori senza operator[], funzioni senza operator(), puntatori intelligenti senza operator->e così via.
Fredoverflow,

Sto dicendo che il potenziale problema del sovraccarico dell'operatore con le operazioni matematiche è piccolo rispetto a quegli operatori. Fare qualcosa di intelligente o di folle con gli operatori matematici potrebbe essere problematico, ma gli operatori che ho elencato, che di solito le persone non pensano come operatori ma piuttosto elementi di linguaggio di base, dovrebbero sempre soddisfare le aspettative definite dalla lingua. []dovrebbe sempre essere un accessor simile a un array e ->dovrebbe sempre significare accedere a un membro. Non importa se si tratta in realtà di un array o di un contenitore diverso o se si tratta di un puntatore intelligente o meno.
Allon Guralnek,

2

Capisco perfettamente che non ti piace l'argomento di Joel sul nascondersi. Neanche io. È davvero molto meglio usare '+' per cose come tipi numerici incorporati o per i tuoi come, per esempio, matrice. Ammetto che è pulito ed elegante per essere in grado di moltiplicare due matrici con '*' invece di '.multiply ()'. E dopo tutto abbiamo lo stesso tipo di astrazione in entrambi i casi.

Ciò che fa male qui è la leggibilità del tuo codice. In casi reali, non nell'esempio accademico di moltiplicazione di matrici. Soprattutto se la tua lingua consente di definire operatori che non sono inizialmente presenti nel nucleo della lingua, ad esempio =:=. A questo punto sorgono molte altre domande. Di cosa si tratta quel maledetto operatore? Voglio dire qual è la precedenza di quella cosa? Qual è l'associatività? In quale ordine viene a =:= b =:= crealmente eseguito?

Questo è già un argomento contro il sovraccarico dell'operatore. Non sei ancora convinto? Il controllo delle regole di precedenza non ti ha richiesto più di 10 secondi? Ok, andiamo oltre.

Se inizi a utilizzare un linguaggio che consente il sovraccarico degli operatori, ad esempio quello popolare il cui nome inizia con 'S', imparerai rapidamente che i progettisti di biblioteche adorano ignorare gli operatori. Naturalmente sono ben istruiti, seguono le migliori pratiche (non c'è cinismo qui) e tutte le loro API hanno perfettamente senso quando le guardiamo separatamente.

Ora immagina di dover usare alcune API che fanno un uso pesante degli operatori che sovraccaricano insieme in un unico pezzo di codice. O ancora meglio: devi leggere un codice legacy del genere. Questo è quando il sovraccarico dell'operatore fa davvero schifo. Fondamentalmente se ci sono molti operatori sovraccarichi in un posto, inizieranno presto a mescolarsi con gli altri caratteri non alfanumerici nel codice del programma. Si mescoleranno con caratteri non alfanumerici che non sono realmente operatori ma piuttosto alcuni elementi grammaticali del linguaggio più fondamentali che definiscono cose come blocchi e ambiti, modellano le dichiarazioni di controllo del flusso o denotano alcune meta-cose. Dovrai mettere gli occhiali e avvicinare gli occhi di 10 cm al display LCD per capire quel disordine visivo.


1
Sovraccaricare gli operatori esistenti e inventare nuovi operatori non sono la stessa cosa, ma +1 da parte mia.
fredoverflow

1

In generale, evito di utilizzare il sovraccarico dell'operatore in modi non intuitivi. Cioè, se ho una classe numerica, il sovraccarico * è accettabile (e incoraggiato). Tuttavia, se ho un dipendente di classe, cosa farebbe l'overloading *? In altre parole, sovraccaricare gli operatori in modi intuitivi che facilitano la lettura e la comprensione.

Accettabile / Incoraggiato:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Non accettabile:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};

1
Moltiplicare i dipendenti? Sicuramente è un'offesa sacrabile, se lo fanno sul tavolo della sala del consiglio, cioè.
gbjbaanb,

1

Oltre a quanto è già stato detto qui, c'è un altro argomento contro il sovraccarico dell'operatore. In effetti, se scrivi +, è abbastanza ovvio che intendi aggiungere qualcosa a qualcosa. Ma non è sempre così.

Lo stesso C ++ fornisce un ottimo esempio di questo caso. Come stream << 1dovrebbe essere letto? flusso spostato a sinistra di 1? Non è affatto ovvio a meno che tu non sappia esplicitamente che << in C ++ scrive anche nello stream. Tuttavia, se questa operazione fosse implementata come metodo, nessuno sviluppatore sano scriverà o.leftShift(1), sarebbe qualcosa di simile o.write(1).

La linea di fondo è che rendendo non disponibile il sovraccarico dell'operatore, il linguaggio fa riflettere i programmatori sui nomi delle operazioni. Anche se il nome scelto non è perfetto, è ancora più difficile interpretare erroneamente un nome piuttosto che un segno.


1

Rispetto ai metodi esplicitati, gli operatori sono più brevi, ma non richiedono parentesi. Le parentesi sono relativamente scomode da digitare. E devi bilanciarli. In totale, qualsiasi chiamata di metodo richiede tre caratteri di semplice rumore rispetto a un operatore. Questo rende l'utilizzo degli operatori molto, molto allettante.
Perché altrimenti qualcuno vorrebbe questo cout << "Hello world":?

Il problema con il sovraccarico è che la maggior parte dei programmatori è incredibilmente pigra e la maggior parte dei programmatori non può permettersi di esserlo.

Ciò che spinge i programmatori C ++ all'abuso del sovraccarico dell'operatore non è la sua presenza, ma l'assenza di un modo più ordinato di eseguire chiamate di metodo. E le persone non hanno solo paura del sovraccarico dell'operatore perché è possibile, ma perché è fatto.
Si noti che ad esempio in Ruby e Scala nessuno ha paura del sovraccarico dell'operatore. A parte il fatto che l'uso degli operatori non è in realtà più breve dei metodi, un'altra ragione è che Ruby limita il sovraccarico degli operatori a un minimo ragionevole, mentre Scala consente di dichiarare i propri operatori rendendo così banale evitare collisioni.


oppure, in C #, per l'utilizzo di + = per associare un evento a un delegato. Non penso che incolpare le caratteristiche del linguaggio per la stupidità del programmatore sia una via costruttiva per il futuro.
gbjbaanb,

0

Il motivo per cui l'overloading dell'operatore è spaventoso, è perché esiste un gran numero di programmatori che non PENSAREbbero mai, il che *non significa semplicemente "moltiplicare", mentre un metodo come foo.multiply(bar)almeno indica immediatamente a quel programmatore che qualcuno ha scritto un metodo di moltiplicazione personalizzato . A quel punto si domandano perché e vanno a indagare.

Ho lavorato con "buoni programmatori" che si trovavano in posizioni di alto livello che avrebbero creato metodi chiamati "CompareValues" che avrebbero preso 2 argomenti e applicato i valori uno dall'altro e restituito un valore booleano. O un metodo chiamato "LoadTheValues" che andrebbe nel database per altri 3 oggetti, ottenendo valori, eseguendo calcoli, modificandolo thise salvandolo nel database.

Se sto lavorando in team con quei tipi di programmatori, so immediatamente di indagare su ciò su cui hanno lavorato. Se hanno sovraccaricato un operatore, non ho assolutamente modo di sapere che lo hanno fatto se non per presumere che lo facessero e andare a cercare.

In un mondo perfetto, o in una squadra con programmatori perfetti, il sovraccarico dell'operatore è probabilmente uno strumento fantastico. Devo ancora lavorare su un team di programmatori perfetti, quindi è per questo che fa paura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.