Переменная C ++, совместно используемая двумя файлами - PullRequest
2 голосов
/ 29 марта 2012

У меня есть 4 файла:

  • shared.h
  • main.cpp
  • something.h
  • thing.cpp

shared.h:

#ifndef SHARED_H
#define SHARED_H

int* sth;

#endif

thing.h:

#ifndef SOMETHING_H
#define SOMETHING_H

class foo
{
   public:
      void printVar();
};

#endif

thing.cpp:

#include <iostream>
#include "something.h"
#include "shared.h"

using namespace std;

void foo::printVar()
{
    cout<<"Foo: "<<*sth<<endl;
};

main.cpp:

#include <cstdlib>
#include <iostream>
#include "shared.h"
#include "something.h"

using namespace std;

int main(int argc, char *argv[])
{
    sth=new int(32);

    foo x;
    cout<<"Main: "<<*sth<<endl;
    x.printVar();

    system("PAUSE");
    return EXIT_SUCCESS;
}

Компилятор возвращает множественное определение *sth;

Я добавил статический модификатор к * sth, и он компилируется, но вылетает.Я изменил печать, чтобы напечатать адреса указателя, и мне вернулась программа:

Main: 0x3e0f20
Foo: 0

Почему указатель foo не назначен?Я хочу назначить его только один раз в основном, а затем поделиться в других файлах ... Как я могу это сделать?Это что-то с модификатором extern?

Спасибо за любые ответы.

Ответы [ 5 ]

5 голосов
/ 29 марта 2012

В shared.h вы хотите сказать:

extern int* sth;

обещать компилятору, что где-то существует.

Затем в один (и только один) из .cpp файлов вам нужно записать:

int* sth;

Чтобы оно действительно существовало. В общем, вы, вероятно, хотите прочитать о разнице между объявлением и определением . Как правило, вы хотите объявлять вещи только в заголовочных файлах, а не определять их.

Когда вы писали static ранее, вы говорили, что переменная с тем же именем существует в каждом файле, но является "локальной" для каждого, то есть sth в main.cpp не будет совпадать с sth в чем-то.cpp.

4 голосов
/ 29 марта 2012

Да, используйте extern. Поместите extern int* sth; в заголовок, а затем в один исходных файлов поместите int* sth;.

extern сообщает компилятору и компоновщику, что фактическое определение переменной / функции находится в другом модуле компиляции (то есть в другом исходном файле).

2 голосов
/ 29 марта 2012

" У меня есть 4 файла ".

Позволь мне остановить тебя прямо там. Я согласен, что у вас есть четыре файла, но это не совсем актуально. У вас есть две единицы перевода . Единица перевода - это квант текста, который подается в ваш компилятор. Ваши единицы перевода, назовем их MAIN и SOMETHING , состоят из результата предварительной обработки ваших файлов main.cpp и something.cpp соответственно.

После предварительной обработки каждая из ваших единиц перевода включает в себя строку int *sth; Эта строка объявляет и определяет переменную sth.

Правило единого определения требует, чтобы во всей вашей программе было ровно одно (не больше, не меньше) определение sth. Для этого у вас должна быть ровно одна строка исходного кода int *sth; ровно в одной единице перевода и столько extern int *sth;, сколько вам требуется.

В вашем случае я бы поставил int *sth; в MAIN и extern int *sth; в ЧТО-ТО. Кроме того, вы можете иметь столько дополнительных копий extern int *sth;, сколько захотите - они ничего не повредят.

Теперь вернемся к вашим четырем файлам. Вы должны положить extern int *sth; в shared.h. Это означает, что MAIN и SOMETHING будут иметь строку extern. Вам также следует ввести int *sth; в main.cpp, чтобы в MAIN было определение.

И вот вы: MAIN и SOMETHING имеют extern строки, поэтому они оба ссылаются на одну и ту же переменную. ГЛАВНАЯ также имеет определение. таким образом, переменная sth имеет ровно одну из них.

<ч /> В сторону : Почему static int *sth; in shared.h делает неправильные вещи?

Поскольку каждая из двух единиц перевода видит свою собственную декларацию static. Объявление static уменьшает связь объявленного имени с этой единственной единицей перевода.

2 голосов
/ 29 марта 2012

Вам нужно пометить sth как extern в заголовке, чтобы сделать его объявлением , и предоставить определение для него в одном из файлов cpp.В противном случае переменная объявлена ​​ в каждой единице компиляции, в которую включен заголовок, что не является желаемым эффектом.

PS Я предполагаю, что вы знаете, почему глобальные переменные не хорошо, верно?

1 голос
/ 29 марта 2012

Вы не хотите делать его статическим глобальным, поскольку это создаст локальный sth в каждой единице перевода, и вы размещаете его только в одной.Вот почему он потерпел крах в Foo::printVar, так как в этой области он был неинициализированным указателем.

Вы хотите объявить его как extern int* sth; в общем заголовке, затем поставить int* sth; перед main или хотя бы в одинпоместите, прежде чем он будет использован.

На самом деле, если вам действительно нужен глобально доступный объект, размещенный в куче, и вы хотите поделиться им, тогда он может быть лучше, чем extern std::shared_ptr<int> sth;.Определите его как std::shared_ptr<int> sth; таким же образом в одном из файлов CPP и вызовите std::make_shared, чтобы выделить его.Таким образом, освобождение памяти будет обрабатываться автоматически, когда последний объект, который ее использует, выходит из области видимости.

...