|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
||||||||||||||||
Можно ли объявлять объекты в заголовочном файле?11.08.2014, 19:16. Показов 9211. Ответов 17
Метки нет (Все метки)
main.cpp
Кликните здесь для просмотра всего текста
17:13:00 **** Incremental Build of configuration Debug for project 1 ****
Info: Internal Builder is used for build g++ -O0 -g3 -Wall -c -fmessage-length=0 -o main.o "..\\main.cpp" g++ -O0 -g3 -Wall -c -fmessage-length=0 -o aa.o "..\\aa.cpp" g++ -o 1.exe main.o aa.o 2.o aa.o:aa.cpp .bss+0x0): multiple definition of `a'main.o:J:\Programming\C++\rotacja\1\Debu g/../main.cpp:11: first defined here 2.o:2.cpp .bss+0x0): multiple definition of `a'main.o:J:\Programming\C++\rotacja\1\Debu g/../main.cpp:11: first defined here collect2.exe: error: ld returned 1 exit status Что я делаю неправильно?
0
|
||||||||||||||||
| 11.08.2014, 19:16 | |
|
Ответы с готовыми решениями:
17
Можно ли объявлять объекты в заголовочном файле? Можно ли объявлять в описании одного класса объекты другого класса
|
|
1 / 1 / 1
Регистрация: 06.08.2014
Сообщений: 68
|
||
| 11.08.2014, 19:21 | ||
|
0
|
||
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
|||||||
| 11.08.2014, 19:39 | |||||||
|
Для того, чтобы этого избежать нужно разнести определение и объявление (сейчас у тебя они, так сказать, в одном флаконе):
0
|
|||||||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 11.08.2014, 19:45 [ТС] | |
|
Спасибо, так получается, но можно ли как - нибудь без extern обойтись?
0
|
|
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
||
| 11.08.2014, 19:57 | ||
|
Или конкретизируй задачу, которую решаешь, - можно будет рассмотреть другие варианты.
0
|
||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 11.08.2014, 20:02 [ТС] | |
|
Эта переменная будет использоваться только несколько раз, но в совершенно разных местах. Было бы хорошо, чтобы она была видна только в тех файлах, где она используется.
0
|
|
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
||||||||
| 11.08.2014, 20:30 | ||||||||
|
Добавлено через 9 минут Это должна быть именно модифицируемая из разных мест переменная? Еще можно предложить сделать две функции интерфейсом к ней:
1
|
||||||||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 11.08.2014, 20:32 [ТС] | |
|
Спасибо. Я слышал где - то, что глобальных переменных лучше избегать, но так и не узнал, почему. Так ли это?
Не лучше ли тогда её в абстрактный класс поместить и отсылаться к этому классу?
0
|
|
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
|||
| 11.08.2014, 20:39 | |||
|
1
|
|||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 11.08.2014, 20:39 [ТС] | |
|
Я только начал графический движок изучать Irrlicht, переменная а - это IrrlichtDevice *device на самом деле, ну и подобные вещи вроде менеджера сцны. Хотел я её с глаз долой убрать, но чтобы всегда под рукой была. Сейчас думаю, может всё - таки лучше делать класс - обёртку с геттерами и сеттерами?
0
|
|
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
|
| 11.08.2014, 20:50 | |
|
_20_,
Класс обычно лучше. Но прежде чем его (их) писать, нужно продумать архитектуру (что с чем взаимодействует, кто выделяет ресурсы, и т.д.). Если этого не сделать, то скорее всего не получится ничего хорошего. Если нет опыта в ООП, сначала лучше книжку почитать толковую по общим моментам. А для тестовых примеров для освоения API движка, как мне кажется, нет смысла городить много кода. Ведь тут главное понять принцип работы. А вот когда соберешься писать свою игру, то тут и пригодятся и знания ООП, и проработка архитектуры.
0
|
|
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 12.08.2014, 02:35 [ТС] | |
|
Немного опыта в ООП есть, но хвалиться не буду, т.к. самоучка. Для тестовых примеров можно всё в одном файле написать, здесь я с Вами согласен. Но ведь ничего плохого не случиться, если я попутно чему - нибудь научусь? Тем более, что по теме, которую мы затронули у меня полнейшая каша в голове. Если Вы не будете против, я постараюсь с ней разобраться с Вашей помощью. Скорее всего я буду Вас о чём - то переспрашивать, но прошу Вас не серчать, это не от того, что я невнимательно Ваши посты читаю, а от того, что я в чём - то не уверен и хочу дополнительно убедиться.
Правильно ли я понял, что без использования extern не получиться сделать видимой переменную для 2х разных файлов?
0
|
|
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
|||
| 12.08.2014, 08:26 | |||
|
Добавлено через 16 минут Я убежден, что программирование имеет мало общего с шаманством, и если ты что-то делаешь, то ты должен быть уверен что это и как это работает. Поэтому я и советовал именно тот вариант, который советовал. Но выбор, как всегда, за тобой, я никого не принуждаю и, естественно, помогу тебе в любой случае, какой бы ты не выбрал
0
|
|||
|
|
|||||||||||
| 12.08.2014, 09:12 | |||||||||||
|
_20_, твою проблему можно разрулить используя namespace
//обычный namespace
//анонимный namespace
1
|
|||||||||||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 12.08.2014, 13:49 [ТС] | |
|
Спасибо. Подводя итог, для реализации доступа к одной и той же переменной можно использовать глобальные переменные extern (нежелательно) и пространства имён namespace. Так же можно сделать переменную статическим полем класса (можно абстрактного), не статическим полем класса, если класс - синглетон, или же приватным полем класса, а доступ раздавать друзьям.
Я ничего не пропустил? Равносильно ли это использование namespace'ов использованию статических полей класс, или есть тут свои тонкости? Реализованны ли в С++ внутренние классы? 2 DrOffset Не могли бы Вы уточнить, что Вы понимаете под сложной задачей и какую именно подготовку Вы считаете минимальной? Без этого, Ваше сообщение видется мне слишком абстрактным для того, чтобы иметь практическое значение.
0
|
|
|
|
||||||||
| 12.08.2014, 20:21 | ||||||||
1
|
||||||||
|
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
|
||||||||||||||||
| 12.08.2014, 23:54 | ||||||||||||||||
Сообщение было отмечено _20_ как решение
Решение![]() Звездочками буду помечать некоторые моменты, которые могут быть дополнены, т.к. я за неимением времени здесь освещаю не все аспекты и тонкости. Начать нужно с причин твоей первоначальной проблемы, "multiple definition". Корень проблемы кроется в особенности метода компиляции исходного кода в С и С++. Он раздельный. Т.е. если мы имеем файлы
пусть a2.h и a1.h подключается в a2.cpp. Что произойдет если мы начнем сборку программы из этих файлов? Препроцессор "вставит" содержимое файла a1.h в a2.cpp. Получится некий временный "файл", который является результатом работы препроцессора - этот "файл" называется единицей трансляции. Далее этот "файл" будет компилироваться компилятором С++ в объектный модуль. Тоже самое происходит с файлом a2.cpp, препроцессор "вставляет" содержимое a2.h и a1.h, получившийся объединенный "файл" (единицу трансляции) компилятор транслирует в объектный модуль. Итого у нас два объектных модуля, которые ничего не знаю друг про друга. Они скомпилированы независимо. Связыванием в единый исполняемый модуль занимается компоновщик (или линкер, или линковщик - кому как нравится). Вот именно компоновщик и выдавал твою ошибку "multiple definition", т.к. переменная int a; определенная в одном общем файле, который независимо был скомпилирован в составе разных единиц трансляции в глазах линкера "раздвоилась" (недопущению такого "размножения" посвящено правило одного определения (ODR)). Вот и получилось у нас две переменные, конфликтующие из-за общего имени. Очень тесно с этим связано понятие linkage(связывание). Оно может быть внутренним(internal), внешним(external) или отсутствовать(no linkage). Внешнее связывание - это когда имя видно линкеру на границе объектного модуля, благодаря этому оно может быть использовано в нескольких модулях как общее. Глобальная переменная по умолчанию имеет внешнее связывание, что и вызвало ошибку в первом посте, т.к. согласно ODR не может быть двух сущностный с одним и тем же именем с внешним связыванием. Внутреннее связывание, это напротив, когда линкер не может использовать имя для связи между разными объектными модулями. Отсутствием связывания характеризуются автоматические переменные, константы времени компиляции, безымянные классы, перечисления и т.п. За подробностями прошу в стандарт С++, параграф 3.5. Первое решение которое я предложил - это extern. extern - это один из спецификаторов класса памяти. Их много: static, auto (до С++11), register, extern и thread_local (в С++11). Все они так или иначе влияют на linkage. Основное* назначение extern в том, что он позволяет указать, что некое имя имеющее (или потенциально способное иметь) внешнее связывание находится где-то (возможно в другом объектном модуле). Где именно выясняет линкер. В данном случае extern int a; выступает в той же роли, что и прототип у функции (кстати свободные функции по умолчанию имеют как раз внешнее связывание, поэтому обычно* нам не надо писать extern перед каждым прототипом - он там подразумевается), создавая лишь объявление. А определение мы предусмотрительно оставили лишь в одной единице трансляции. После этих действий ошибка пропала, т.к. мы объяснили линкеру, что переменная определена в одном месте, а вынесенное в общее место определение с extern является декларацией доступа, ссылкой (linker reference) на нее. Если залезть глубже, то в тех объектных файлах где было определение с extern будет добавлена информация, что переменная не определена и определение может находиться в другом месте. Можно подробнее, например, здесь посмотреть (инфа для unix, но принцип не меняется). Кстати если мы так и не напишем ни одного определения, а оставим лишь объявления с extern, то линкер имя естественно не найдет и ошибка уже будет другая - "undefined reference". Теперь пара слов о namespace. namespace сам по себе означенную проблему не решает. В С++ есть понятие - глобальное пространство имен. Если не предпринято никаких действий, то глобальные имена определяются в нем. То, что мы сузили область видимости, добавив свой namespace, ничего по сути не изменило и, если рассмотреть твой первый пример с добавлением твоей переменной "int a" в namespace, то ничего не изменится, ошибка будет такая же как и прежде. Теперь отдельно про анонимный namespace. Действительно, он как бы решает твою проблему, убирая ошибку. Но вот тут самое время вспомнить про linkage. Анонимный namespace определяет для имени internal linkage (внутренее связывание). Это означает, что в каждой из единиц трансляции, куда попадет такая переменная, определенная в анонимном namespace будет содержать свой экземпляр этой переменной, недоступный линкеру для связывания. Т.е. переменных так и будет две как и прежде, просто из-за внутреннего связывания линкер не будет их видеть при компоновке. Естественно изменения с одной переменной в одном объектном модуле никак не затронут другую. Практически тоже самое* можно сказать про переменную, объявленную со спецификатором static (не в классах, а глобально), он тоже определяет внутреннее связывание и эффект в нашем случае будет идентичный. Все остальные перечисленные в цитате ниже способы Статические же поля классов, например такие:
Подчеркну, что эти все рассуждения ведутся в контексте потенциального решения тобой сложной задачи, например такой, как написание игры, я же правильно понял, иначе зачем изучать API игрового движка? И это опять же не значит, что нельзя одновременно что-то писать и учиться - можно, но нужно быть готовым к тому, что все это окажется работой в корзину и все придется переделывать. И возможно не один раз. Тестовые же примеры и обучающие минипрограммки нужно писать в любом случае, о них сейчас речи нет. Движок является относительно сложной программной системой и его понимание в той или иной степени упирается и в знания языка. При проектировании любого API, в том числе и API движка, применяются идиомы или концепции, которые строятся на каких-либо языковых особенностях. Чтобы эффективно использовать API нужно так или иначе представлять как работает инструмент С или С++. Именно с учетом этого был дан совет подтянуть С++, в противовес попытке изучить все скопом "за одно". Хотя я и не отрицал и не отрицаю, что, возможно, у кого-то и так получится. В общем что-то я увлекся. Надеюсь что-то полезное ты для себя вынесешь.
8
|
||||||||||||||||
|
22 / 56 / 9
Регистрация: 29.09.2011
Сообщений: 618
|
|
| 16.08.2014, 01:03 [ТС] | |
|
Спасибо Вам большое за столь развёрнутый ответ. Хоть Вы и говорите, что много не раскрыли, определённую ясность в моё понимание С++ Вы внесли. На данный момент у меня не осталось никаких вопросов, чтобы спрашивать.
Плюсы я не сразу не ставил по той причине, что я готов ставить плюсы каждому, кто мне помогает за каждый его пост. То есть, если применительно к Вам, за каждый пост в теме готов поставить Вам плюс, хотя Вам это и не важно. Поэтому я обычно рассавляю плюсы после обсуждения за самые полезные посты.
0
|
|
| 16.08.2014, 01:03 | |
|
Помогаю со студенческими работами здесь
18
Где правильно объявлять и инициализировать объекты/ MFC Как правильно объявлять статические константные объекты в классе?
Функции в заголовочном файле Ошибка в заголовочном файле Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Символьное дифференцирование
igorrr37 13.02.2026
/ *
Программа принимает математическое выражение в виде строки и выдаёт его производную в виде строки и вычисляет
значение производной при заданном х
Логарифм записывается как: (x-2)log(x^2+2) -. . .
|
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
|
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу,
и светлой Луне.
В мире
покоя нет
и люди
не могут жить в тишине.
А жить им немного лет.
|
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила»
«Время-Деньги»
«Деньги -Пуля»
|
|
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога
Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
|
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога
Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
|
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога
Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
|
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
|