Domanda

Se due file C ++ hanno definizioni diverse di classi con lo stesso nome, quindi quando vengono compilate e collegate, qualcosa viene lanciato anche senza un avvertimento. Ad esempio,

// a.cc
class Student {
public:
    std::string foo() { return "A"; }
};
void foo_a()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}
.

Se compilato e collegato insieme utilizzando G ++, entrambi verranno emessi "A" (se A.CC precede B.CC nell'ordine della riga di comando).

Un argomento simile è qui . Vedo che il namespace risolverà questo problema ma non so perché il linker non attira nemmeno un avvertimento. E se una definizione della classe ha una funzione extra che non è definita in un'altra, ad esempio se B.CC viene aggiornato come:

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
    std::string bar() { return "K"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << stu.bar() << std::endl;
}
.

Allora Stu.bar () funziona bene. Grazie a chiunque possa dirmi come funziona il compilatore e il linker in tale situazione.

Come domanda extra, se le classi sono definite nei file di intestazione, se sono sempre avvolti con spazio dei nomi senza nome per evitare tale situazione? C'è qualche effetto collaterale?

È stato utile?

Soluzione

Questa è una violazione della regola definizione (C ++ 03, 3.2 / 5 "una regola di definizione"), che dice (tra le altre cose):

.

Ci possono essere più di una definizione di un tipo di classe (clausola 9), ... In un programma a condizione che ogni definizione sia visualizzata in un altro Unità di traduzione e fornito alle definizioni soddisfano quanto segue requisiti. Dato una tale entità denominata D definita in più di una Unità di traduzione, quindi

    .
  • ogni definizione di D è composta dalla stessa sequenza di token;

    Se vivi la regola definizione, il comportamento non è definito (il che significa che le cose strane possono accadere).

    Il linker vede più definizioni di Student::foo(): uno nel file oggetto di A e uno in B. Tuttavia non si lamenta a questo; Seleziona solo uno dei due (come succede, il primo che si incontra). Questa gestione "morbida" delle funzioni duplicate apparentemente avviene solo per le funzioni in linea. Per le funzioni non in linea, il linker si lamenta di più definizioni e si rifiuterà di produrre un eseguibile (potrebbero esserci opzioni che rilassano questa restrizione). Entrambi GNU ld e MSVC's Linker si comportano in questo modo.

    il comportamento ha senso; Le funzioni in linea devono essere disponibili in ogni unità di traduzione in cui vengono utilizzate. E nel caso generale devono avere versioni non in linea disponibili (nel caso in cui la chiamata non sia innata o se l'indirizzo della funzione è prelevato). inline è davvero solo un pass gratuito intorno alla regola di una sola definizione - ma per il lavoro, tutte le definizioni in linea devono essere uguali.

    Quando guardo le discariche dei file dell'oggetto, non vedo nulla ovvio che spiega a me come il linker sa che una funzione è autorizzata ad avere più definizioni e altri, ma sono sicuro che ce ne sono alcuni bandiera o disco che fa proprio questo. Sfortunatamente, trovo che i lavori del collegamento del collegamento e dei dettagli del file dell'oggetto non siano particolarmente ben documentati, quindi il meccanismo preciso rimarrà probabilmente un mistero per me.

    come per la tua seconda domanda:

    .

    Come domanda aggiuntiva, se le classi sono definite nei file di intestazione, dovrebbero Saranno sempre avvolti con spazio dei nomi senza nome per evitare tale situazione? C'è qualche effetto collaterale?

    Quasi certamente non vuoi fare ciò ogni classe sarebbe un tipo distinto in ogni unità di traduzione, quindi tecnicamente istanze della classe non potevano essere passate da un'unità di traduzione a un'altra (da puntatore, riferimento o copia ). Inoltre, finiresti con più istanze di qualsiasi membro statico. Probabilmente non funzionerebbe bene.

    mettili in diversi settori dei nomi denominati.

Altri suggerimenti

Hai violato la regola di definizione per le definizioni di classe e la lingua specificamente vieta di farlo.Non è richiesto per il compilatore / linker di avvisare o diagnosticare, e tale scenario non è certamente garantito di funzionare come previsto in questo caso.

.

Ma non so perché il linker non spara nemmeno un avviso.

Le violazioni della regola di definizione di una definizione non richiedono la diagnostica perché per implementare la diagnostica, il linker dovrebbe dimostrare che le due definizioni non sono, infatti, equivalenti.

Questo è facile una volta che le classi hanno diverse dimensioni, un numero diverso di membri o diverse classi di base.Ma non appena tutti questi fattori sono uguali, è possibile che Ancora hanno le definizioni dei membri diverse per esempio, e il linker dovrebbe utilizzare un'introspezione avanzata per confrontarli.Anche se ciò fosse sempre possibile (e non sono sicuro che lo sia, poiché i file dell'oggetto potrebbero essere compilati con diverse opzioni, portando a diversi risultati), è certamente molto inefficiente.

Di conseguenza, il Linkeer accetta solo che ciò che lancio non viola l'ODR.Non è una situazione perfetta, ma bene.

Penso che la tua domanda "extra" sia un indizio per la domanda principale.

Se capisco la tua domanda extra, allora penso che tu faccia non vuoi avvolgerle in uno spazio dei nomi, poiché se sei #include la stessa classe in più file .cc, probabilmente vuoiUtilizzare una sola copia di ciascun metodo, anche se sono definiti all'interno della classe.

Questo (tipo di) spiega perché ottieni solo una versione di ciascuna funzione nel tuo esempio principale.Mi aspetto che il linker stia solo assumendo che le due funzioni siano identiche - generate dalla stessa fonte #inclusata.Sarebbe bello se il linker avrebbe rilevato quando erano diversi e rilasciano un avvertimento, ma immagino che sia difficile.

Come indica Mark B, la risposta pratica è "semplicemente non andare lì".

Oltre alla violazione di una regola di definizione, non vedi compilatore si lamenta a causa di NOME MANGLING IN C ++

Modifica: Come sottolineato da Konrad Rudolph: i nomi mangiati in questo caso sarebbero uguali.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top