Недопустимый неполный тип visual studio
Трудно сказать, чем руководствовались умные дядьки, создавая стандарт языка, в котором оператор delete можно применить к объекту, тип которого в этой точке удаления может быть еще полностью не определен.
В языке и без этого хватает undefined behavior (читай -- граблей), а описанное выше послабление приводит к появление еще одного, описанного в разделе стандарта 5.3.5/5:
"If the object being deleted has incomplete class type at the point of deletion and the complete class has a
non-trivial destructor or a deallocation function, the behavior is undefined."
Теоретически, компилятор очень легко может детектировать эту потенциально опасную ситуацию, и выдавать соотвествующее предупреждение, однако полагаться на это не стоит, т.к. компиляторы бывают разные, да и вообще, кто читает эти глупые ворнинги? Разве что те, кому лень их отключать.
Шутка.
Чаще всего проблема удаления неполного типа возникает рядом с хорошо известным паттерном Pimpl, который удобнее всего реализовывать на boost::scoped_ptr. Ужжжасный класс std::auto_ptr можно посоветовать использовать разве что врагу, тем более, что в новый стандарт языка умные указатели из boost'а скорее всего войдут.
Смотрим на приведенный ниже код, содержащийся в трех разных файлах -- ClassA.h, ClassA.cpp, main.cpp.
Представим себе, что деструктор ClassA::~ClassA мы не определяли (его тело пустое, т.к. за автоматичское удаление содержимого указателей m_shared и m_scoped отвечают деструкторы умных указателей). Итак, деструктор мы не определяли, а это значит, что его за нас должен сгенерировать компилятор. Где он это сделает? Очевидно, что при компиляции файла main.cpp, деструктор для ClassA понадобиться при выходе из тела функции main(). Значит, именно здесь будет генерироваться тело ~ClassA, которое, в свою очередь, потянет за собой генерацию тел деструкторов для шаблонов shared_ptr и scoped_ptr для типа ClassB, которые в своем теле будут вызывать delete для указателей этого типа. Который (смотрим внимательно) в этой точке у нас является неполным типом.
Ага, приплыли.
Что произойдет, если мы попытаемся собрать приведенный код без деструктора?
Мы увидим, как добрые люди, писавшие умные указатели для boost, позаботились о нас.
Откуда берется эта ошибка? Умные указатели в boost вызывают delete не на прямую, а через вспомогательный шаблон boost::checked_delete, который проверят, что в момент вызова используемый тип полностью определен (делается элементарно, через sizeof(T)), иначе мы получаем приведенную выше ошибку.
Завершая разговор, упомяну еще один небольшой нюанс. Класс boost::shared_ptr более толерантен к описанной выше проблеме, чем boost::scoped_ptr. Вызов функции reset(T *p) (который, естественно, происходит в точке, где тип T определен полностью) приводит к генерации нужного кода для деструкции объекта, и решать проблему с помощью пустого деструктора для класса с Pimpl обычно нет необходимости.
Есть 2 класса реализации и объявления которых раскиданы по файлам. Я пытаюсь из класса Elements обратиться к полю _gameInt но ничего не выходит. Файлы выглядят так:
В функции foo() в выделенной строке компилятор ругается:
Если у вас используется какой-то тип в заголовочном файле, который вы после подключаете в файл который использует этот тип, вы получите циклическую ошибку. Объявите прототип класса в заголовке, а в файле реализации подключите требуемый заголовок, таким образом вы избавитесь от зацикливания, и сохраните чистоту кода.
2 ответа 2
В такой ситуации вам надо принять волевое решение о том, какой тип (и, соответственно, заголовок) считать более низкоуровневым, а какой - более высокоуровневым. При включении заголовочных файлов, разрешается включать только более низкоуровневые заголовки в более высокоуровневые заголовки, но не наоборот. При необходимости упомянуть в низкоуровневом заголовке тип из более высокоуровневого заголовка просто делается предварительное объявление типа.
Например, в вашем случае естественно было бы считать тип Elements и заголовок Elements.h более низкоукровневым, а тип Game и заголовок Game.h - более высокоуровневым.
В такой ситуации в заголовочном файле Elements.h мы делаем предварительное объявление класса Game , но Game.h в Elements.h ни в коем случае не включаем
А заголовочный файл Game.h реализуем "обычным" образом, т.е. ни о чем не беспокоясь включаем в него Elements.h
Далее, в файл реализации Elements.cpp спокойно включаем все, что нам нужно, т.е. и Elements.h , и Game.h
А в Game.cpp достаточно включить Game.h (но ничего страшного не случится, если вы включите и Elements.h тоже)
При этом у вас есть полная свобода в реализации класса Game - вы можете реализовывать его методы где угодно, в т.ч. в Game.h , т.е. можете обойтись без Game.cpp вообще. Также вы можете содержать в классе Game подобъекты класса Element , то есть напрямую, а не через указатель.
А вот с классом Elements у вас будут ограничения - полным типом тип Game будет являться только в Elements.cpp и все методы, требующие полноты типа Game придется реализовывать именно там. То есть класс Elements может содержать только непрямые ссылки (указатели) на класс Game .
При таком разбиении вы не можете реализовывать методы Elements , работающие с Game , как inline функции. Но при необходимости и это достижимо заведением дополнительного включаемого файла.
Прежде всего я хочу извиниться за мой английский. Боюсь, это не мой родной язык. Я схожу с ума от этого кода.
Я делаю небольшой движок для игр. Я реализовал класс под названием «процесс», который будет наследовать все элементы, составляющие игру. То, что происходит, заключается в том, что при реализации двух из этих элементов, которые наследуются от «процесса», компилятор выдает мне следующую ошибку:
Я читал, что многие из проблем в этом отношении были опубликованы на этом форуме ранее, и я понимаю, что ошибка в том, что я неправильно использую класс (класс «процесс»), потому что не выполняю инстанцирование должным образом или потому, что в нем чего-то не хватает. класс (обязательный метод не реализован).
Но когда я читаю и проверяю свой код, я не могу найти, где я делаю ошибку. Кто-нибудь может увидеть в чем я не прав?
Я думаю, что вам не нужны остальные классы. Но если вам нужно, вы можете найти в ссылке на GitHub выше.
Решение
У вас есть случай циклической зависимости здесь: Engine класс использует process а также process использования Engine , Это часто показатель неверной архитектуры.
Если все, что вам нужно, это решить проблему компиляции, предварительное объявление должно помочь: удалите либо «process.h» из «engine.h» (или наоборот) и объявите
в заголовке «engine.h».
Другие решения
У вас есть круговая проблема включения.
process.h включает в себя engine.h а также engine.h включает в себя process.h , Вам нужно разорвать этот цикл. Обычно это делается с использованием предварительных объявлений и добавления включений в исходные файлы.
На первый взгляд кажется, что вы можете сделать предварительное объявление процесса в engine.h и удалите включение.
Примечание: часто вы включаете заголовок в файл заголовка класса и в исходный файл того же класса, но это не обязательно, поскольку заголовок уже включает его.
Пример выпуска
В вышеупомянутом есть круговая проблема включения; включенные охранники не имеют ничего общего с решением этой проблемы. Кроме того, так как Foo использует только указатель на Bar Например, для прерывания цикла можно использовать предварительную декларацию.
Определение: ужезаявлениеНо пока нетОпределенияВведите. Неполные типы нельзя использовать для определения переменных или членов класса, но допустимо определять указатели или ссылки с неполными типами. (Из C ++ Primer P274)
Для типов:
Заявление (справочное заявление)Сообщите компилятору, что этот тип уже существует, но в настоящее время компилятор не знает, сколько байтов памяти необходимо выделить объектам этого типа.
Определение (определение определения): Описывает детали этого типа, чтобы компилятор мог знать, сколько памяти нужно выделить для этого типа объекта.
Другими словами,Определение неполного типа состоит в том, что полный тип не был определен, (Компилятор еще не знает, сколько памяти выделить для этого типа объекта)
В C / C ++ есть несколько форм неполных типов void、Массив неопределенной длины иСтруктура и объединение с неопределенным содержанием。
Void является особенным, потому что это встроенный неполный тип, и пользователи не могут определить его полностью, так что компилятор знает, сколько памяти должно быть выделено для объектов этого типа. А поводом для void приложения являются только возвращаемое значение и параметры специальных функций. (Например: malloc ())
В настоящее время все вышеперечисленные типы a, b, c являются неполными.
Если сразуОпределить тип объектаилиИспользуйте этот типВсе неправильно
Помните, что при использовании этого типа или определении объектов этого типа вы должны убедиться, что этот тип был определен.
Но неполный тип допускает следующие операции
- Указатель на неполный тип (определяется память, занимаемая указателем, 4 байта для 32 машин и 8 байтов для 64-битных машин, поэтому компилятор может создать его напрямую)
- Неполные типы ссылок (ссылочные типы не занимают единиц хранения, поэтому их можно создавать)
- Имена типов для неполных типов
- Функция, которая возвращает неполный тип (например: void test () Примечание: f () перед test () только объявлен, но не определен)
- Неполные типы параметров функции (например, void test (int, int &) Примечание: имя формального параметра не было указано при объявлении test ())
Выше специальная функция, используйте это предложение, чтобы решить головоломку
После того, как функция объявлена, прежде чем она определена. Это законно и действительно, чтобы быть названным.
1. В обычных сценах, когда тип объявлен глобально, но не определен, объекты, которые определяют тип и используют тип (например: см. Объяснение определения)
2. Две структуры содержат объекты типа друг друга
Определение в классе a сообщит об ошибке. Неполные типы не допускаются
Определение в относительном классе b не ошибется, потому что перед выполнением test_a; класс a был полностью определен ранее.
3. Друзья класса используют объект определения класса, но друзья класса определяются перед классом.
1. Улучшить гибкость кода
Когда компилятор встречает первое объявление, он считает, что str является неполным типом, а когда он встречает второе объявление, str объединяется в полный тип.
Например: когда нам нужен кусок памяти, но мы не знаем, на сколько подать заявку. Объявите массив char b [] в файле .h, мы полностью объявим его в файле .c, вы можете легко изменить длину массива. И нет необходимости снова компилировать заголовочный файл при компиляции.
2. Неполные типы позволяют использовать указатели и ссылочные типы. В течение периода тип пакета гарантирован.
Этот вопрос уже задавался здесь раньше, но в моем конкретном случае решения не работали (или я не знаю, как перенести их в свой код). У меня есть что-то вроде этого (упрощено до рассматриваемых методов)
При компиляции я получаю ошибку «недопустимое использование неполного типа 'const class View'» в строке v = newView.clone ();
В чем может быть причина этого, пожалуйста? Кроме того, есть ли способ включить YearView.hpp в файл Calendar.hpp? Я хотел бы использовать его, чтобы по умолчанию установить 'v' в конструкторе Calendar для определенного типа YearView.
Ищите Включите охранников и не пересылайте объявления, когда вам это не нужно!
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно.
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей.
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то.
Ответы 2
Calendar.hpp включает View.hpp , который включает Calendar.hpp . Это тот цикл, который вам нужно прервать.
И вы делаете это с помощью нет, включая Calendar.hpp в файл View.hpp . Это должно работать, потому что View.hpp фактически не использует ничего из Calendar.hpp .
Итак, файл View.hpp должен выглядеть примерно так:
Форвардные объявления View в других файлах заголовков не нужны, поскольку вы включаете файл View.hpp . И вам нужно включить файл View.hpp , потому что вы используете класс View не только для объявлений.
Спасибо, вы привели меня к решению. View.hpp действительно использует тип Calendar в другом методе, но достаточно предварительного объявления, мне действительно не нужно было его включать.
@chododom Если вы вызываете функцию-член Calendar , вам понадобится файл заголовка. Я предлагаю разбить код на отдельные заголовочные и исходные файлы. Затем вы могли бы использовать форвардные объявления в файле заголовка, а затем включить полные файлы заголовков в исходные файлы.
Some programmer dude
@chododom Кроме того, это одна из причин, почему так важно использовать Минимальный, полный и проверяемый пример.
Some programmer dude
Some programmer dude
У вас есть циркулярное включение между calendar.hpp и view.hpp , я полагаю, вы пытались разорвать этот цикл, добавив форвардные объявления.
Проблема в том, что вы используете заранее объявленные классы в своих заголовках. Объявленный вперед класс может использоваться только в объявлениях, вы не можете создавать новые экземпляры класса или вызывать какие-либо его методы.
Если вы переместите реализацию своих методов в файлы cpp, это должно решить вашу проблему.
Другие вопросы по теме
Почему родительская таблица не обновляется новой записью при вставке записи в дочернюю таблицу (с использованием стратегии таблицы для каждого класса)
Как родительский класс «перечисляет» получает доступ к аргументу, не заданному как переменная экземпляра?
Похожие вопросы
Как выполнить проверку type_check во время компиляции и компилировать только части моего класса, если тип моих членов класса соответствует типам?
Читайте также: