Коли використовувати extern в C ++


398

Я читаю "Поміркуй на C ++", і він просто представив externдекларацію. Наприклад:

extern int x;
extern float y;

Я думаю, що я розумію значення (декларація без визначення), але мені цікаво, коли це виявиться корисним.

Хтось може навести приклад?


1
Мені довелося давати визначення externкілька разів. Інструменти Microsoft створили помилку посилання для відсутніх символів, коли таблиці в іншому вихідному файлі були лише визначені. Проблема полягала в тому, що таблиця була, constі компілятор C ++ просував її staticв блок перекладу. Дивіться, наприклад, ariatab.cppі kalynatab.cpp.
jww

2
І я вважаю, що відповідь Ніка є правильною, тому що він єдиний, хто, мабуть, відповів на питання C ++. Всі інші, схоже, перейшли до питання С.
jww

Відповіді:


519

Це корисно, коли у вас є глобальні змінні. Ви заявляєте про існування глобальних змінних у заголовку, так що кожен вихідний файл, що включає заголовок, знає про нього, але вам потрібно лише "визначити" його один раз в одному з вихідних файлів.

Для уточнення, використовуючи extern int x;підказує компілятору, що об'єкт типу, що intназивається, xіснує десь . Це не робота компіляторів, щоб знати, де вона існує, вона просто повинна знати тип та назву, щоб вона знала, як ними користуватися. Після того, як всі вихідні файли будуть скомпільовані, лінкер вирішить усі посилання xна одне визначення, яке він знайде в одному з компільованих вихідних файлів. Щоб вона працювала, визначення xзмінної повинно мати те, що називається "зовнішня зв'язок", що в основному означає, що її потрібно оголосити поза функцією (у тому, що зазвичай називають "область файлу") і без staticключового слова.

заголовок:

#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

джерело 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();
}

джерело 2:

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

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

15
Дякую. Отже, якщо я оголошу глобальну змінну у файлі заголовка без зовнішнього ключового слова, вихідні файли, що містять заголовок, не бачать його?
Aslan986

23
не слід оголошувати глобальний vars у заголовку, тому що тоді, коли 2 файли містять один і той же файл заголовка, він не посилатиметься (Linker видасть помилку щодо "дублюючого символу")
kuba

63
@ Aslan986: Ні, трапляється щось гірше. Кожен вихідний файл, що включає заголовок, матиме власну змінну, тому кожен вихідний файл буде збиратися незалежно, але лінкер скаржиться на те, що два вихідні файли матимуть однакові глобальні ідентифікатори.
dreamlax

7
Якщо ви не використовуєте слово "extern", то тепер змінна існує. Коли ви використовуєте "екстерн", це "ей, є цей вар десь ще". Вибачте, що не відповідаю, чи це визначення, чи декларація, оскільки я завжди плутаюсь щодо цих двох.
куба

3
@CCJ: включити захист працює лише для вихідного файлу, що включає його. Він зупиняє те, що один і той же заголовок два рази включається в один і той же вихідний файл (на випадок, якщо інші заголовки також включають його тощо). Тож навіть із включенням охоронців, кожен вихідний файл, що включає заголовок, все ще матиме своє власне визначення.
dreamlax

172

Це корисно, коли ви поділяєте змінну між декількома модулями. Ви визначаєте його в одному модулі, а в інших використовуєте зовнішній.

Наприклад:

у file1.cpp:

int global_int = 1;

у file2.cpp:

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

39
Ця відповідь більш правильна, ніж прийнята, оскільки в ній не використовується файл заголовка, і чітко зазначено, що він корисний лише при обміні між декількома модулями. Для великих програм краще використовувати, наприклад, клас ConfigManager.
Зак

1
Чи є які-небудь gotchas, коли залучені простори імен, global_intє у глобальній просторі імен, якби я використовував його у file2.cpp в якомусь розділі простору імен, я повинен був би це обґрунтувати правильно? тобтоnamespace XYZ{ void foo(){ ::global_int++ } };
jxramos

8
@Zac: З іншого боку, не оголошуючи глобальну змінну у заголовку, ви ненавмисно зробили набагато складніше визначити, де вона насправді визначена. Зазвичай, якщо ви бачите глобальну змінну, декларовану в abc.h, є хороший шанс, що вона буде визначена в abc.cpp. Хороший IDE завжди допоможе, але добре організований код - це завжди краще рішення.
dreamlax

без externфайлу2.cpp, все ще може отримати доступ до global_intпісля включення. чому мені це потрібно?
TomSawyer

62

Вся справа в зв'язках .

Попередні відповіді дали хороші пояснення щодо extern.

Але я хочу додати важливий момент.

Ви питаєте про externв С ++ не в C , і я не знаю , чому немає відповіді згадувати про той випадок , коли externприходить з constв C ++.

У C ++ constзмінна має внутрішню зв'язок за замовчуванням (не як C).

Тож цей сценарій призведе до помилки зв’язку :

Джерело 1:

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

Джерело 2:

extern const int global; //declaration

Це має бути таким:

Джерело 1:

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

Джерело 2:

extern const int global; //declaration

2
Чому це неправильно, коли він працює в c ++, не включаючи в поняття частину "extern"?
Головний перемикач

1
Я, здається, не стикаюся з такою помилкою зв’язку у VIsual Studio з Visual Micro. Що я пропускаю?
Craig.Feied

1
@ lartist93 @ Craig.Feied Я вважаю, що вам може знадобитися ще раз ретельно перевірити. Навіть у випадку, якщо компілятор не повідомляє про помилку посилання, чи можете ви перевірити, чи обидва об'єкти в обох джерелах однакові без externвизначення? Ви можете зробити це, роздрукувавши значення globalв джерелі 2.
Тревор,

3
Підтвердьте, що в MSVS 2018 є помилка посилання, якщо externвона опущена const int global = 255;.
Євг

13

Це корисно, коли ви хочете мати глобальну змінну. Ви визначаєте глобальні змінні в якомусь вихідному файлі і оголошуєте їх зовнішніми у файлі заголовка, щоб будь-який файл, що включає цей заголовок, побачив ту саму глобальну змінну.


Так чи інакше це не дуже звучить OOP, я ставлю їх до класу
синглтон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.