Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException
вообще.
См. также: A хороший список лучших практик
Я бы добавил, очень важно, хорошо использовать модификатор final
. Использование "окончательной" модификатор, когда это применимо в Java
Сводка:
final
для обеспечения хорошей инициализации. @NotNull
и @Nullable
if("knownObject".equals(unknownObject)
valueOf()
поверх toString (). StringUtils
StringUtils.isEmpty(null)
. Хотя стандартный C ++ не предъявляет таких требований, некоторые компиляторы требуют, чтобы все шаблоны функций были доступны в каждой единице перевода, в которой они используются. Фактически, для этих компиляторов тела функции шаблона должны быть доступны в файле заголовка. Повторюсь: это означает, что эти компиляторы не позволяют определять их в файлах без заголовков, таких как файлы .cpp. Чтобы уточнить, в C ++ ese это означает, что this:
// ORIGINAL version of xyz.h
template <typename T>
struct xyz
{
xyz();
~xyz();
};
НЕ будет удовлетворен этими определениями ctor и dtors:
// ORIGINAL version of xyz.cpp
#include "xyz.h"
template <typename T>
xyz<T>::xyz() {}
template <typename T>
xyz<T>::~xyz() {}
, потому что его использование:
// main.cpp
#include "xyz.h"
int main()
{
xyz<int> xyzint;
return 0;
}
приведет к ошибке. Например, с Comeau C ++ вы получите:
C: \ export> como xyz.cpp main.cpp С ++ 'ing xyz.cpp ... Comeau C / C ++ 4.3.4.1 (29 мая 2004 г., 23:08:11) для MS_WINDOWS_x86 Авторское право 1988-2004 Comeau Computing. Все права защищены. РЕЖИМ: нестрогие предупреждения microsoft C ++ С ++ в main.cpp ... Comeau C / C ++ 4.3.4.1 (29 мая 2004 г., 23:08:11) для MS_WINDOWS_x86 Авторское право 1988-2004 Comeau Computing. Все права защищены. РЕЖИМ: нестрогие предупреждения microsoft C ++ main.obj: ошибка LNK2001: неразрешенный внешний символ xyz
:: ~ xyz () [с T1 = int] главный.obj: ошибка LNK2019: неразрешенный внешний символ xyz :: xyz () [с T1 = int], на который имеется ссылка в функции _main aout.exe: фатальная ошибка LNK1120: 2 нерешенных внешних фактора
потому что в xyz.cpp не используются ни ctor, ни dtor, следовательно, нет необходимости создавать экземпляры оттуда. Хорошо это или плохо, но так работают шаблоны.
Один из способов обойти это - явно запросить создание экземпляра xyz
в этом примере xyz
. Попыткой грубой силы это может быть добавлено в xyz.cpp, добавив эту строку в конце:
template xyz<int>;
, которая запрашивает создание (всех) xyz
экземпляров. Это вроде как не в том месте, поскольку это означает, что каждый раз, когда вызывается новый тип xyz, необходимо изменять файл реализации xyz.cpp. Менее навязчивый способ избежать этого файла - создать другой:
// xyztir.cpp
#include "xyz.cpp" // .cpp file!!!, not .h file!!
template xyz<int>;
Это все еще несколько болезненно, потому что все еще требует ручного вмешательства каждый раз, когда создается новый xyz. В нетривиальной программе это может быть необоснованная потребность в обслуживании.
Другой способ подойти к этому - #include "xyz.cpp"
в конец xyz.h:
// xyz.h
// ... previous content of xyz.h ...
#include "xyz.cpp"
Вы, конечно, можете буквально перенести (вырезать и вставить) содержимое xyz.cpp до конца xyz.h, таким образом избавляясь от xyz.cpp; это вопрос организации файлов, и, в конце концов, результаты предварительной обработки будут одинаковыми, поскольку тела ctor и dtor будут в заголовке и, следовательно, будут включены в любой запрос компиляции, поскольку для этого будет использоваться соответствующий заголовок.В любом случае у этого есть побочный эффект: теперь каждый шаблон находится в вашем файле заголовка. Это может замедлить компиляцию и привести к раздуванию кода. Один из способов приблизиться к последнему - объявить рассматриваемые функции, в данном случае ctor и dtor, как встроенные, поэтому вам потребуется изменить xyz.cpp в текущем примере.
Кроме того, некоторые компиляторы также требуют, чтобы некоторые функции были определены внутри класса, а не за его пределами, поэтому в случае этих компиляторов потребуется дополнительная настройка описанной выше настройки. Обратите внимание, что это проблема компилятора, а не стандартного C ++, поэтому не все компиляторы требуют этого. Например, Comeau C ++ этого не делает и не должен. Посетите http://www.comeaucomputing.com/4.0/docs/userman/ati.html для получения подробной информации о нашей текущей настройке. Короче говоря, Comeau C ++ поддерживает множество моделей, в том числе ту, которая приближается к намерениям ключевого слова export (как расширение), а также даже поддерживает сам экспорт.
Наконец, обратите внимание, что ключевое слово C ++ export призвано облегчить исходный вопрос. Однако в настоящее время Comeau C ++ - единственный компилятор, который публикуется для поддержки экспорта. См. http://www.comeaucomputing.com/4.0/docs/userman/export.html и http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff. txt для получения дополнительных сведений. Будем надеяться, что по мере того, как другие компиляторы достигнут соответствия стандарту C ++, эта ситуация изменится. В приведенном выше примере использование экспорта означает возврат к исходному коду, который вызвал ошибки компоновщика, и внесение изменений: объявление шаблона в xyz.h с ключевым словом export:
// xyz.h
export
// ... ORIGINAL contents of xyz.h ...
ctor и dtor в xyz.cpp будут экспортированы просто благодаря #includeing xyz.h, что он уже делает. Итак, в этом случае вам не нужен xyztir.cpp,ни запрос на создание экземпляра в конце xyz.cpp, и вам не нужно вручную вносить ctor или dtor в xyz.h. С помощью командной строки, показанной ранее, возможно, что компилятор сделает все за вас автоматически.
См. этот объяснение его использования
, Довольно много компиляторов не поддерживают его или потому что это является слишком новым или в случае gcc - потому что они не одобряют.
Это сообщение описывает стандартную поддержку многих компиляторов. поддержка Visual Studio нового C / стандарты C++?
См. здесь и здесь для обработки Herb Sutter предмета.
В основном: экспорт был реализован в [только 112] один компилятор - и в той реализации, экспорт на самом деле увеличивает связь между шаблонным определением и объявлением, тогда как единственная точка в представлении экспорта должна была уменьшить эту связь.
Вот почему большинство компиляторов не беспокоится. Я думал бы, что они просто удалят экспорт из языка в C++ 0x, но я не думаю, что они сделали. Возможно, однажды будет хороший способ реализовать экспорт, который имеет надлежащее использование.
Экспорт является функцией, которая представляет круговую зависимость между компоновщиком и компилятором. Как другие отметили, это позволяет одной единице перевода содержать определение шаблона, используемого в другом. Компоновщик будет первым для обнаружения этого, но этому нужен компилятор для инстанцирования шаблона. И это включает очень твердую работу, как поиск имени.
Comeau представил его сначала, приблизительно 5 лет назад IIRC. Это работало вполне хорошо над первой бета-версией, которую я получил. Даже тестовые сценарии как A< 2> использование B< 2> использование A< 1> с помощью B< 1> с помощью A< 0>, обработанный, если шаблоны A и B прибыли из различного TU's. Несомненно, компоновщик неоднократно вызывал компилятор, но все поиски имени работали хорошо. Инстанцирование A< 1> найденный именами от A.cpp, которые были невидимы в B.cpp.
Помещать его просто:
export
позволяет Вам разделить объявление (т.е. заголовок) из определения (т.е. код), когда Вы пишете свои шаблонные классы. Если export
не поддерживается Вашим компилятором тогда, необходимо положить объявление и определение на одно место.
Единственными компиляторами, которые поддерживают экспортируемые шаблоны в данный момент (насколько я знаю) является Comeau, тот, который шел с Разработчиком Borland C++ X, но не текущим Разработчиком C++ и Intel (по крайней мере, неофициально, если не официально, не уверенный).