2010-02-24 22 views
47

nếu tôi xác định varibles thường xuyên của tôi trong phần đầu của tôi như thế này ...biến liên tục không làm việc trong tiêu đề

extern const double PI = 3.1415926535; 
extern const double PI_under_180 = 180.0f/PI; 
extern const double PI_over_180 = PI/180.0f; 

tôi nhận được lỗi sau

1>MyDirectX.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj 
1>MyDirectX.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj 
1>MyDirectX.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj 

nhưng Nếu tôi loại bỏ những hằng số từ tiêu đề và đặt chúng trong tài liệu bao gồm tiêu đề như thế này ...

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

Nó hoạt động

Có ai có ý tưởng về những gì tôi có thể làm sai không ??

Cảm ơn

+0

Có lẽ bạn nên viết '180.0' thay vì' 180.0f' vì bạn đang xử lý số nhân đôi thay vì phao. Đồng thời đổi tên 'PI' thành một cái gì đó độc đáo hơn. 'PI' được sử dụng trong nhiều thư viện dưới dạng macro và nếu bạn sử dụng nó, bạn có thể nhận được kết quả kỳ quặc. – thebretness

+0

Mất 'extern' và bạn sẽ ổn. – sellibitze

+1

@sellibitze: Không phải trong C, trong đó các đối tượng 'const' có liên kết bên ngoài theo mặc định, có nghĩa là' extern' không thay đổi bất kỳ thứ gì. – AnT

Trả lời

114

Vấn đề là bạn xác định đối tượng có liên kết bên ngoài trong tệp tiêu đề. Dự kiến, khi bạn đưa tệp tiêu đề đó vào nhiều đơn vị dịch, bạn sẽ nhận được nhiều định nghĩa của cùng một đối tượng có liên kết bên ngoài, đó là lỗi.

Cách thích hợp để làm điều đó tùy thuộc vào ý định của bạn.

(1) Bạn có thể đặt định nghĩa của mình vào tệp tiêu đề nhưng đảm bảo rằng chúng có liên kết nội bộ.

Trong C mà sẽ đòi hỏi một rõ ràng static

static const double PI = 3.1415926535; 
static const double PI_under_180 = 180.0f/PI; 
static const double PI_over_180 = PI/180.0f; 

Trong C++ static là không bắt buộc (vì trong C++ const đối tượng có liên kết nội bộ theo mặc định)

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

(2) Hoặc bạn chỉ có thể đặt khai báo không xác định chỉ vào tệp tiêu đề và đặt các định nghĩa vào một (và chỉ một) tập tin thực hiện

Các tờ khai trong tiêu đề tập tin phải bao gồm rõ ràng externkhông initializer

extern const double PI; 
extern const double PI_under_180; 
extern const double PI_over_180; 

và định nghĩa trong một thực hiện tập tin nên trông như sau

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

(rõ ràng extern in các định nghĩa là tùy chọn, nếu các khai báo trên đứng trước các định nghĩa trong cùng một đơn vị dịch).

Phương pháp nào bạn sẽ chọn tùy thuộc vào ý định của bạn.

Phương pháp đầu tiên giúp trình biên dịch tối ưu hóa mã dễ dàng hơn vì nó có thể thấy giá trị thực tế của hằng số trong mỗi đơn vị dịch. Nhưng đồng thời, khái niệm bạn nhận được các đối tượng liên tục độc lập riêng biệt trong mọi đơn vị dịch thuật. Ví dụ: &PI sẽ đánh giá đến địa chỉ khác trong mỗi đơn vị dịch.

Phương pháp thứ hai tạo ra thực sự hằng số toàn cầu hằng số, tức là các đối tượng cố định duy nhất được toàn bộ chương trình chia sẻ. Ví dụ: &PI sẽ đánh giá đến cùng một địa chỉ trong từng đơn vị dịch. Nhưng trong trường hợp này trình biên dịch chỉ có thể thấy các giá trị thực trong một và chỉ một đơn vị dịch, điều này có thể cản trở việc tối ưu hóa.

+1

+1. Chỉ muốn đề cập đến rằng trong C, biến 'static const' không phải là một biểu thức liên tục như bạn cần cho kích thước của một mảng chẳng hạn. Tôi đoán đó là lý do tại sao '# define' phổ biến hơn trong C. – sellibitze

+3

+1 cho mô tả tốt và dạy tôi điều gì đó mà tôi không biết: "trong các đối tượng cố định C++ có liên kết nội bộ theo mặc định" –

+0

@sellibitze: Có, nhưng điều này chủ yếu trở thành vấn đề với hằng số tích phân. Tác động đối với các đối tượng hằng số không tách rời là không đáng kể, nếu tồn tại tất cả. – AnT

5

Lớp extern lưu trữ cho họ là gần như chắc chắn là nguyên nhân của vấn đề bạn đang nhìn thấy. Nếu bạn loại bỏ nó, mã có lẽ sẽ ổn (ít nhất là trong khía cạnh này).

Chỉnh sửa: Tôi vừa nhận thấy rằng bạn đã gắn thẻ thẻ này là cả C và C++. Về mặt này, C và C++ thực sự khá khác nhau (nhưng từ các thông báo lỗi, bạn dường như biên dịch như C++, chứ không phải C). Trong C++, bạn muốn xóa extern, bởi vì (theo mặc định) các biến số const có lớp lưu trữ static. Điều đó có nghĩa là mỗi tệp nguồn (đơn vị dịch) sẽ có "bản sao" riêng của biến đó và sẽ không có bất kỳ xung đột nào giữa các định nghĩa trong các tệp khác nhau. Vì bạn (có thể) chỉ sử dụng các giá trị, không xử lý chúng như là các biến, có nhiều "bản sao" sẽ không làm tổn hại gì cả - không ai trong số chúng được cấp không gian lưu trữ.

Trong C, extern là khá khác nhau và việc xóa extern sẽ không tạo ra bất kỳ khác biệt thực sự nào, vì chúng sẽ là extern theo mặc định. Trong trường hợp này, bạn thực sự cần phải khởi tạo các biến trong chính xác một vị trí và khai báo chúng bên ngoài trong tiêu đề. Ngoài ra, bạn có thể thêm lớp lưu trữ static mà C++ sẽ thêm theo mặc định khi/nếu bạn xóa extern khỏi tiêu đề.

1

Bạn cần khai báo các đối tượng trong tiêu đề và sau đó xác định chúng trong một trong các tệp mã của bạn. Nếu bạn không khai báo chúng ở bất cứ nơi nào, thì có một lỗi liên kết khi nó cố gắng gắn kết khai báo với định nghĩa thực tế. Bạn cũng có thể thoát khỏi bằng cách sử dụng câu lệnh #ifdef để có một định nghĩa trong tiêu đề.

Đảm bảo chúng được khai báo trong tiêu đề được bao gồm bởi tất cả mọi người cần chúng và đảm bảo chúng được xác định chính xác một lần.

Jacob

8

extern có nghĩa là 'thực tế' định nghĩa của biến là ở nơi khác, và trình biên dịch nên tin tưởng rằng mọi thứ sẽ treo lên lúc liên kết. Có định nghĩa nội tuyến với các extern là lạ và là những gì làm tăng lên chương trình của bạn. Nếu bạn muốn có chúng là extern, chỉ cần xác định chúng chính xác một lần ở nơi khác trong chương trình của bạn.

1

Nếu bạn muốn xác định hằng số trong tệp tiêu đề, hãy sử dụng static const. Nếu bạn sử dụng extern, trình liên kết có quyền khiếu nại về nhiều định nghĩa vì mỗi tệp bao gồm nguồn sẽ cung cấp bộ nhớ cho biến nếu bạn chỉ định giá trị.

2

Có vẻ như tệp tiêu đề đó đang được đưa vào nhiều lần. Bạn cần thêm lính gác.

Ở phía trên cùng của mỗi tập tin tiêu đề, bạn nên có một cái gì đó như:

#ifndef MY_HEADER_FILE_NAME_H 
#define MY_HEADER_FILE_NAME_H 

... 

// at end of file 
#endif 

Nếu bạn đang sử dụng g ++ hoặc MSVC sau đó bạn chỉ có thể thêm:

#pragma once 

Ở phía trên của mỗi tiêu đề tập tin, nhưng đó không phải là 100% di động.

Ngoài ra, bạn không nên xác định các hằng số trong các tập tin tiêu đề, chỉ khai báo:

// In header file 
extern const int my_const; 


// In one source file 
const int my_const = 123; 
+3

Bao gồm các nhân viên bảo vệ chỉ bảo vệ chống lại việc đưa nhiều người vào cùng một tệp. Thiếu chúng sẽ tạo ra một vấn đề tại thời gian biên dịch. Anh ta đang gặp sự cố vào thời gian liên kết, vì cố gắng xác định cùng một biểu tượng một lần trong mỗi tệp. –

+0

Tôi đã giải quyết điều đó trong câu trả lời của mình. –

0

trong tuyên bố const toàn cầu trong tiêu đề gây rằng mỗi đơn vị biên soạn bao gồm Hader này sẽ có định nghĩa của riêng định nghĩa toàn cầu với cùng Tên. Sau đó, trình liên kết không thích điều đó.

Nếu bạn thực sự cần chúng trong tiêu đề thì có thể Bạn nên khai báo chúng là tĩnh.

2

Vấn đề là bạn đang khởi tạo các biến trong tệp tiêu đề; điều này tạo ra một tuyên bố xác định, được lặp lại trong mọi tệp bao gồm tiêu đề đó, do đó có nhiều lỗi định nghĩa.

Bạn muốn có một phi khai -defining (không initializer) trong file header, và đặt các tuyên bố xác định trong một của các tập tin thực hiện.

2

Rất nhiều câu trả lời không chính xác bên dưới. Những người đúng là những người nói với bạn để loại bỏ các extern như sellibitze cũng đã nói trong bình luận của mình là chính xác.

Bởi vì đây là những tuyên bố const, không có vấn đề với định nghĩa trong tiêu đề. C++ sẽ inline một const cho một kiểu built-in trừ khi bạn cố gắng lấy địa chỉ của nó (một con trỏ tới const), trong trường hợp nó sẽ khởi tạo nó với liên kết static, sau đó bạn có thể nhận được nhiều instantiations trong các mô-đun riêng biệt, trừ khi bạn mong đợi tất cả các con trỏ đến cùng một const để có cùng một địa chỉ, đây không phải là một vấn đề.

+0

MSVC có hành vi khác với bạn mong đợi. Bạn có thể nhận được các biểu tượng trùng lặp nếu bạn khai báo một biến là 'const' trong một tiêu đề. Bạn cần phải sử dụng cả hai 'const tĩnh' để đảm bảo liên kết nội bộ. Có thể đó là lỗi trình biên dịch. – jww

+0

@jww: trong câu hỏi, nó đã được báo cáo rằng nó hoạt động mà không cần tĩnh. Các hành vi cho C _does_ khác nhau, nhưng kể từ khi mã trong câu hỏi liên kết, nó phải có sử dụng C + + biên dịch. Tuy nhiên, câu hỏi được gắn thẻ cả C và C++, vì vậy tôi cho rằng tôi nên đề cập đến cả hai, nhưng câu hỏi không phải là về sự khác biệt giữa C và C++, vì vậy tôi giả sử chỉ gắn thẻ sai. Câu hỏi là 7 tuổi, vì vậy nó không đảm bảo cập nhật. – Clifford

0

Câu hỏi cũ, quả thật vậy, nhưng thiếu một câu trả lời hữu ích.

Có thể lừa MSVC vào chấp nhận hằng tĩnh trong tiêu đề đơn giản bằng cách gói những người trong một "dummy" lớp mẫu:

template <typename Dummy = int> 
struct C { 
    static const double Pi; 
}; 

template <typename Dummy = int> 
const double C<Dummy>::Pi = 3.14159; 

Bây giờ, C <> :: PI có thể được truy cập từ các nơi khác. Không có định nghĩa lại; hằng số có thể truy cập trực tiếp trong mỗi đơn vị biên dịch mà không có tối ưu hóa thời gian liên kết ưa thích. Macro có thể được triển khai để tiếp tục tạo điều kiện thuận lợi cho phương pháp này (mặc dù các macro là ác).

+0

Có bất kỳ nhược điểm nào khi sử dụng phương pháp này không? – PolyMesh

+0

Một chút nhược điểm cú pháp, như đã đề cập - một sẽ phải tham chiếu đến Pi là C <> :: Pi trong suốt mã. Nhưng sau đó, các hằng số toàn cầu lỏng lẻo sẽ tránh được tốt hơn (vì vậy MathConst <> :: Pi chỉ kém nhẹ hơn MathConst :: Pi). – oakad

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