Khi nào nên sử dụng extern trong C ++


398

Tôi đang đọc "Think in C ++" và nó vừa giới thiệu bản externkhai. Ví dụ:

extern int x;
extern float y;

Tôi nghĩ rằng tôi hiểu ý nghĩa (khai báo mà không có định nghĩa), nhưng tôi tự hỏi khi nó chứng minh hữu ích.

Ai đó có thể cung cấp một ví dụ?


1
Tôi đã phải cung cấp một định nghĩa với externnhiều lần. Các công cụ của Microsoft đã tạo ra lỗi liên kết cho các ký hiệu bị thiếu khi các bảng trong tệp nguồn khác chỉ được xác định. Vấn đề là, bảng đã constvà trình biên dịch C ++ đã quảng bá nó statictrong đơn vị dịch thuật. Xem, ví dụ, ariatab.cppkalynatab.cpp.
jww

2
Và tôi nghĩ câu trả lời của Nik là câu trả lời đúng bởi vì anh ấy là người duy nhất xuất hiện đã trả lời câu hỏi C ++. Mọi người khác dường như đã lạc đề đến một câu hỏi C.
jww

Câu trả lời:


519

Điều này có ích khi bạn có các biến toàn cục. Bạn khai báo sự tồn tại của các biến toàn cục trong một tiêu đề, để mỗi tệp nguồn bao gồm tiêu đề biết về nó, nhưng bạn chỉ cần định nghĩa lại một lần trong một trong các tệp nguồn của bạn.

Để làm rõ, việc sử dụng extern int x;cho trình biên dịch biết rằng một đối tượng thuộc loại intđược gọi là xtồn tại ở đâu đó . Đây không phải là công cụ biên dịch để biết nó tồn tại ở đâu, nó chỉ cần biết loại và tên để nó biết cách sử dụng nó. Khi tất cả các tệp nguồn đã được biên dịch, trình liên kết sẽ giải quyết tất cả các tham chiếu xđến một định nghĩa mà nó tìm thấy trong một trong các tệp nguồn được biên dịch. Để nó hoạt động, định nghĩa của xbiến cần phải có cái gọi là liên kết bên ngoài, có nghĩa là nó cần phải được khai báo bên ngoài hàm (tại cái thường được gọi là phạm vi tập tin phạm vi) và không có statictừ khóa.

tiêu đề:

#ifndef HEADER_H
#define HEADER_H

// any source file that includes this will be able to use "global_x"
extern int global_x;

void print_global_x();

#endif

nguồn 1:

#include "header.h"

// since global_x still needs to be defined somewhere,
// we define it (for example) in this source file
int global_x;

int main()
{
    //set global_x here:
    global_x = 5;

    print_global_x();
}

nguồn 2:

#include <iostream>
#include "header.h"

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}

15
Cảm ơn bạn. Vì vậy, nếu tôi khai báo một biến toàn cục trong tệp tiêu đề không có từ khóa extern, thì các tệp nguồn bao gồm tiêu đề không nhìn thấy nó?
Aslan986

23
bạn không nên khai báo vars toàn cầu trong một tiêu đề, bởi vì sau đó khi 2 tệp bao gồm cùng một tệp tiêu đề, nó sẽ không liên kết (trình liên kết sẽ phát ra lỗi về "biểu tượng trùng lặp")
kuba

63
@ Aslan986: Không, điều gì đó tồi tệ hơn xảy ra. Mỗi tệp nguồn bao gồm tiêu đề sẽ có biến riêng , vì vậy mỗi tệp nguồn sẽ biên dịch độc lập nhưng trình liên kết sẽ khiếu nại vì hai tệp nguồn sẽ có cùng số nhận dạng toàn cục.
dreamlax

7
Khi bạn không sử dụng từ "extern", thì bây giờ biến tồn tại. Khi bạn sử dụng "extern", đó là "hey có var này ở nơi khác". Xin lỗi về việc không trả lời liệu đó là một định nghĩa hay tuyên bố, vì tôi luôn bối rối về hai điều này.
kuba

3
@CCJ: bảo vệ bao gồm chỉ hoạt động cho tệp nguồn bao gồm nó. Nó dừng tiêu đề tương tự được bao gồm hai lần trong cùng một tệp nguồn (chỉ trong trường hợp các tiêu đề khác cũng bao gồm nó, v.v.). Vì vậy, ngay cả với các trình bảo vệ bao gồm, mỗi tệp nguồn bao gồm tiêu đề vẫn sẽ có định nghĩa riêng.
dreamlax

172

Nó rất hữu ích khi bạn chia sẻ một biến giữa một vài mô-đun. Bạn xác định nó trong một mô-đun và sử dụng extern trong các mô-đun khác.

Ví dụ:

trong file1.cpp:

int global_int = 1;

trong file2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;

39
Câu trả lời này đúng hơn câu trả lời được chấp nhận, vì nó không sử dụng tệp tiêu đề và nó nói rõ rằng nó chỉ hữu ích khi chia sẻ giữa một vài mô-đun. Đối với các ứng dụng lớn hơn, tốt hơn là sử dụng ví dụ như lớp ConfigManager.
Zac

1
Có bất kỳ vấn đề nào khi không gian tên được tham gia, global_inttrong không gian tên toàn cầu, nếu tôi sử dụng nó trong file2.cpp trong một số phần không gian tên tôi phải phạm vi chính xác? tức lànamespace XYZ{ void foo(){ ::global_int++ } };
jxramos

8
@Zac: Mặt khác, bằng cách không khai báo biến toàn cục trong tiêu đề, bạn đã vô tình làm cho việc xác định nơi thực sự được xác định là khó khăn hơn nhiều. Thông thường nếu bạn thấy một biến toàn cục được khai báo abc.h, rất có thể nó sẽ được xác định abc.cpp. Một IDE tốt sẽ luôn giúp ích, nhưng mã được tổ chức tốt luôn là giải pháp tốt hơn.
dreamlax

không có externtrong file2.cpp, vẫn có thể truy cập global_intsau khi bao gồm. tại sao tôi cần phải có nó?
TomSawyer

62

Đó là tất cả về mối liên kết .

Các câu trả lời trước cung cấp giải thích tốt về extern.

Nhưng tôi muốn thêm một điểm quan trọng.

Bạn hỏi về externtrong C ++ không phải trong C và tôi không biết tại sao không có câu trả lời đề cập tới trường hợp khi externđi kèm với constbằng C ++.

Trong C ++, một constbiến có liên kết nội bộ theo mặc định (không giống như C).

Vì vậy, kịch bản này sẽ dẫn đến lỗi liên kết :

Nguồn 1:

const int global = 255; //wrong way to make a definition of global const variable in C++

Nguồn 2:

extern const int global; //declaration

Nó cần phải như thế này:

Nguồn 1:

extern const int global = 255; //a definition of global const variable in C++

Nguồn 2:

extern const int global; //declaration

2
Tại sao nó sai trong khi nó hoạt động trong c ++ mà không bao gồm 'extern' trong phần định nghĩa?
Shifter trưởng

1
Tôi dường như không gặp phải lỗi liên kết đó trong VIsual Studio với Visual Micro. Tôi đang thiếu gì?
Craig.Feied

1
@ lartist93 @ Craig.Feied Tôi tin rằng bạn có thể cần kiểm tra lại một cách cẩn thận. Ngay cả trong trường hợp trình biên dịch không thông báo lỗi liên kết, bạn có thể kiểm tra cả hai đối tượng trong cả hai nguồn đều giống nhau mà không có externđịnh nghĩa không? Bạn có thể làm điều đó bằng cách in ra giá trị của globalnguồn 2.
Trevor

3
Xác nhận, trong MSVS 2018 có một lỗi liên kết nếu externđược bỏ qua trong const int global = 255;.
Evg

13

Điều này rất hữu ích khi bạn muốn có một biến toàn cục. Bạn xác định các biến toàn cục trong một số tệp nguồn và khai báo chúng ở bên ngoài trong tệp tiêu đề để bất kỳ tệp nào bao gồm tệp tiêu đề đó sẽ thấy cùng một biến toàn cục.


Dù sao, điều này không có vẻ rất OOP, tôi sẽ đưa chúng vào một lớp đơn ... hoặc một hàm trả về giá trị tĩnh cục bộ ...
RzR
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.