2013-03-20 13 views
11

Diciamo che ho due file:Namespace e membri della classe statici che collega

/** 
* class.cpp 
*/ 
#include <stdio.h> 
class foo 
{ 
private: 
     int func(); 
}; 

int foo::func(void) 
{ 
     printf("[%s:%d]: %s\n", __FILE__, __LINE__, __FUNCTION__); 
     return -1; 
} 

e

/** 
* main.cpp 
*/ 
#include <stdio.h> 
namespace foo 
{ 
     int func(void); 
} 
int main(void) 
{ 
     int ret = foo::func(); 
     printf("[%s:%d]: ret=%d\n", __FILE__, __LINE__, ret); 
     return 0; 
} 

compilato come segue:

g++ -o a.out main.cpp class.cpp 

C'è un output di file eseguibile:

[class.cpp:15]: func 
[main.cpp:14]: ret=-1 

E infine la mia domanda:

Perché questo codice di esempio compilato senza errori e siamo in grado di richiamare privata metodo classe foo?

Compilato con gcc 4.6.3 ma non solo. So che il compilatore non distingue questi due simboli (func funzione dal namespace foo e funzione privata foo da classe foo). Uscita da nm:

nm class.o 
00000000 T _ZN3foo4funcEv 
00000017 r _ZZN3foo4funcEvE12__FUNCTION__ 
     U printf 

nm main.o 
     U _ZN3foo4funcEv 
00000000 T main 
     U printf 

Vorrei chiedere se questo comportamento è corretto o no? IMHO questo non è un comportamento corretto e non è affatto sicuro (interrompe l'incapsulamento).

Vorrei ricordare che il compilatore di Visual Studio 2008 non collega questi due simboli.

risposta

2

Perché hai definito foo() come membro di uno spazio dei nomi in main.cpp che è come il compilatore lo ha trattato. La distinzione tra classe/struttura/spazio dei nomi pubblico/privato ecc. Dipende dal compilatore che conosce la definizione della funzione - qui hai deliberatamente deciso di ingannarlo.

Il linker non conosce tali distinzioni, risolve semplicemente i nomi dei simboli e, nel caso del compilatore, la decorazione dei nomi delle funzioni si risolve nello stesso modo. Il modo in cui i nomi dei simboli sono decorati non è specificato in C++, quindi si tratta di un comportamento perfettamente valido.

3

Perché il compilatore non si lamenta?

Si noti che "classe", "struct" e "spazio dei nomi" definiscono tutti uno spazio dei nomi per quanto riguarda il compilatore. Quindi il compilatore decora i simboli di conseguenza. Si lamenterebbe se si definiscono sia la classe che lo spazio dei nomi nello stesso file, tuttavia non è il caso qui.

Perché il linker non si lamenta?

Il modo in cui è stato scritto il codice, lascia il func() definito namespace foo più debole rispetto al func() definito class foo. Fondamentalmente, lo definito in namespace foo è solo una firma senza implementazione.Si può vedere che è lasciato al linker di risolvere il simbolo in fase di esecuzione perché l'implementazione non è in main.cpp:

nm main.o 
     U _ZN3foo4funcEv 
//Here^^^^ 

In questo modo, dal momento che i nomi dello spazio dei nomi e di classe è capitato di essere lo stesso (con conseguente stessi simboli per foo::func), il linker risolve il simbolo al momento del collegamento, trova una definizione forte con lo stesso simbolo e link a esso.

Se si dovesse attuare la func() nella namespace foo così:

/** 
* main.cpp 
*/ 
#include <stdio.h> 

namespace foo 
{ 
    int func(void) { 
     printf("NM_FOO [%s:%d]: %s\n", __FILE__, __LINE__, __FUNCTION__); 
     return -1; 
    }; 
} 
int main(void) 
{ 
    int ret = foo::func(); 
    printf("[%s:%d]: ret=%d\n", __FILE__, __LINE__, ret); 
    return 0; 
} 

Avreste visto che il linker si lamenta con:

duplicate symbol foo::func()  in: 
/var/folders/.../class.o 
/var/folders/.../main.o 
ld: 1 duplicate symbol for architecture x86_64 

Se si guarda alla main.o questo tempo che vedresti:

0000000000000064 T __ZN3foo4funcEv 
0000000000000158 S __ZN3foo4funcEv.eh 
00000000000000e0 s __ZZN3foo4funcEvE12__FUNCTION__ 
0000000000000000 T _main 
0000000000000128 S _main.eh 
       U _printf 

E classe.o:

0000000000000000 T __ZN3foo4funcEv 
00000000000000a0 S __ZN3foo4funcEv.eh 
0000000000000080 s __ZZN3foo4funcEvE12__FUNCTION__ 
       U _printf 

entrambi i quali definiscono lo stesso simbolo di funzione ugualmente forte, causando l'errore del linker.

Ricordare che il linker non conosce la differenza tra uno spazio dei nomi e una classe. Risolve i simboli presenti nel codice oggetto. Si lamenterà solo se si verificano forti re-definizioni. Una o più definizioni più deboli con una definizione forte è perfettamente valida nel mondo dei linker.

+0

Ok, ho capito tutto ma la domanda è: PERCHÉ esiste un comportamento del compilatore gcc. Il compilatore di Windows è in grado di distinguere questi simboli. Namespace e classe non sono la stessa cosa, sono io wright? Il compilatore dovrebbe conoscere tutta la differenza tra uno spazio dei nomi e una classe, non dovrebbe? – pako

+0

Il compilatore lo sa, ma il linker no. Tuttavia, il compilatore converte ciascun file in un file oggetto separato. Il modo in cui hai definito i due file è che sono completamente separati dal compilatore. Quello che VS fa è buono, ma non è richiesto che sia fatto. Quindi, non è fatto da gcc. Clang compila anche questo senza lamentarsi a proposito. – meyumer

+0

So che il compilatore sa, ma quello che voglio dire è che il compilatore genera totalmente gli stessi simboli per quelle funzioni, anche se sono totalmente differenti. Quindi forse gcc dovrebbe fare lo stesso del compilatore VS? Potrebbe evitare alcuni potenziali errori. – pako

Problemi correlati