2013-03-20 45 views
11

phép nói rằng tôi có hai tập tin:Namespace và các thành viên lớp tĩnh liên kết

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

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

/** 
* 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; 
} 

biên soạn như sau:

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

Có một đầu ra từ tập tin thực thi:

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

Và cuối cùng câu hỏi của tôi:

Tại sao mẫu mã này biên soạn mà không cần bất kỳ lỗi nào và chúng tôi có thể gọi tin phương pháp lớp foo?

Được biên dịch bằng gcc 4.6.3 nhưng không chỉ. Tôi biết rằng trình biên dịch không phân biệt hai ký hiệu (func chức năng từ namespace foo và chức năng riêng foo từ lớp foo). Output từ nm:

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

nm main.o 
     U _ZN3foo4funcEv 
00000000 T main 
     U printf 

Tôi muốn hỏi liệu hành vi này là đúng hay không? IMHO điều này là không đúng hành vi và nó không phải là an toàn ở tất cả (phá vỡ đóng gói).

Tôi muốn đề cập rằng trình biên dịch từ studio hình ảnh 2008 không liên kết hai biểu tượng đó.

Trả lời

2

Vì bạn đã xác định foo() làm thành viên của không gian tên trong main.cpp đó là cách trình biên dịch xử lý nó. Sự khác biệt giữa class/struct/namespace public/private etc phụ thuộc vào trình biên dịch biết định nghĩa của hàm - ở đây bạn đã cố tình đặt ra để đánh lừa nó.

Trình liên kết không biết về sự phân biệt như vậy, nó chỉ đơn giản là giải quyết tên biểu tượng và trong trường hợp trình biên dịch của bạn trang trí tên hàm sẽ giống nhau. Cách biểu tượng tên được trang trí là không xác định trong C++ vì vậy đây là hành vi hoàn toàn hợp lệ.

3

Tại sao trình biên dịch không khiếu nại?

Lưu ý rằng "lớp", "cấu trúc" và "không gian tên" đều xác định một không gian tên theo như trình biên dịch có liên quan. Vì vậy, trình biên dịch trang trí các biểu tượng cho phù hợp. Nó sẽ khiếu nại nếu bạn định nghĩa cả lớp và vùng tên trong cùng một tệp, tuy nhiên nó không phải là trường hợp ở đây.

Tại sao trình liên kết không khiếu nại?

Cách bạn đã viết mã, rời func() quy định tại namespace foo yếu hơn so với các quy định tại func()class foo. Về cơ bản, func() được xác định trong namespace foo chỉ là chữ ký mà không cần triển khai.Bạn có thể thấy rằng nó là trái với các mối liên kết để giải quyết các biểu tượng trong thời gian chạy vì việc thực hiện không có trong main.cpp:

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

Bằng cách này, vì không gian tên và lớp tên đã xảy ra là như nhau (dẫn đến cùng một ký tự cho foo::func), trình liên kết giải mã biểu tượng tại thời gian liên kết, tìm ra định nghĩa mạnh có cùng biểu tượng và liên kết với nó.

Nếu bạn đã thực hiện func() trong namespace foo cũng như:

/** 
* 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; 
} 

Bạn sẽ thấy rằng các mối liên kết phàn nàn với:

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

Nếu bạn nhìn vào main.o này thời gian bạn sẽ thấy:

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

Và class.o:

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

cả hai đều xác định cùng một biểu tượng chức năng không kém phần mạnh, dẫn đến lỗi trình liên kết.

Hãy nhớ rằng, trình liên kết không biết về sự khác biệt giữa không gian tên và lớp. Nó giải quyết các ký hiệu nằm trong mã đối tượng. Nó sẽ chỉ phàn nàn nếu các định nghĩa lại mạnh xảy ra. Một hoặc nhiều định nghĩa yếu hơn với một định nghĩa mạnh là hoàn toàn tốt trong thế giới liên kết.

+0

Ok, tôi hiểu mọi thứ nhưng câu hỏi là tại sao có hành vi như vậy của trình biên dịch gcc. Trình biên dịch Windows có thể phân biệt các ký hiệu này. Không gian tên và lớp học không giống nhau, tôi có sao không? Trình biên dịch nên biết tất cả sự khác biệt giữa một không gian tên và lớp, phải không? – pako

+0

Trình biên dịch biết, nhưng trình liên kết thì không. Tuy nhiên, trình biên dịch chuyển đổi từng tệp thành một tệp đối tượng riêng biệt. Cách bạn định nghĩa hai tệp là chúng hoàn toàn tách biệt với trình biên dịch. Những gì VS làm là tốt, nhưng không bắt buộc phải làm. Do đó, không được thực hiện bởi gcc. Clang cũng biên dịch nó mà không phàn nàn bằng cách này. – meyumer

+0

Tôi biết rằng trình biên dịch biết, nhưng những gì tôi có nghĩa là trình biên dịch tạo ra hoàn toàn các biểu tượng giống nhau cho các chức năng đó, ngay cả khi chúng hoàn toàn khác nhau. Vì vậy, có lẽ gcc nên làm tương tự như trình biên dịch VS? Nó có thể tránh được một số lỗi tiềm năng. – pako

Các vấn đề liên quan