Другое событие NullPointerException
возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals
для гарантированного непустого объекта.
Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null
.
Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Причина перечисление не может быть вперед объявлено, состоит в том, что, не зная значения, компилятор не может знать устройство хранения данных, требуемое для перечислимой переменной. Компилятору C++ позволяют определить пространство настоящего хранения на основе размера, необходимого для содержания всех определенных значений. Если все, что видимо, является предописанием, единица перевода не может знать, какой размер ресурса хранения будет выбран - это мог быть символ или интервал или что-то еще.
<час>От Раздела 7.2.5 из Стандарта C++ ISO:
базовый тип перечисления является целочисленным типом, который может представить все значения перечислителя, определенные в перечислении. Это определяется реализацией, какой целочисленный тип используется в качестве базового типа для перечисления за исключением того, что базовый тип не должен быть больше, чем
int
, если значение перечислителя не сможет вписатьсяint
илиunsigned int
. Если список перечислителя пуст, базовый тип - то, как будто перечисление имело единственный перечислитель со значением 0. Значениеsizeof()
относилось к перечисляемому типу, объект перечисляемого типа или перечислитель, является значениемsizeof()
, относился к базовому типу.
Начиная с вызывающая сторона к функции должна знать, что размеры параметров правильно устанавливают стек вызовов, количество перечислений в списке перечисления должно быть известно перед прототипом функции.
Обновление: В C++ 0X синтаксис для предисловия, объявляющего перечислимые типы, был предложен и принят. Вы видите предложение в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
Вы определяете перечисление для ограничения возможных значений элементов типа к ограниченному набору. Это ограничение должно быть осуществлено во время компиляции.
, вперед объявление того, что Вы будете использовать 'ограниченный набор' позже, не добавит значения: последующий код должен знать возможные значения для пользы из него.
, Хотя компилятор касавшийся размера перечислимого типа, , намерение из перечисления теряется, когда Вы вперед объявляете это.
Поскольку перечисление может быть интегральным размером переменного размера (компилятор решает, которые измеряют данное перечисление, имеет), указатель на перечисление может также иметь переменный размер, так как это - целочисленный тип (символы имеют указатели различного размера на некоторых платформах, например).
, Таким образом, компилятор не может даже позволить, Вы передать - объявляете перечисление и пользователя указатель на него, потому что даже там, ему нужен размер перечисления.
Мое решение Вашей проблемы было бы к также:
1 - используют интервал вместо перечислений: Объявите свой ints в анонимном пространстве имен в Вашем файле CPP (не в заголовке):
namespace
{
const int FUNCTIONALITY_NORMAL = 0 ;
const int FUNCTIONALITY_RESTRICTED = 1 ;
const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}
, Поскольку Ваши методы являются частными, никто не смешает с данными. Вы могли даже пойти далее, чтобы протестировать, если кто-то отправляет Вам недопустимые данные:
namespace
{
const int FUNCTIONALITY_begin = 0 ;
const int FUNCTIONALITY_NORMAL = 0 ;
const int FUNCTIONALITY_RESTRICTED = 1 ;
const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
const int FUNCTIONALITY_end = 3 ;
bool isFunctionalityCorrect(int i)
{
return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
}
}
2: создайте полный класс с ограниченными инстанцированиями константы, как сделанный в Java. Вперед объявите класс, и затем определите его в файле CPP и instanciate только подобные перечислению значения. Я сделал что-то как этот в C++, и результат столь не удовлетворял, как желаемый, поскольку требовался некоторый код для моделирования перечисления (конструкция копии, оператор =, и т.д.).
3: Как предложено прежде, используйте конфиденциально заявленное перечисление. Несмотря на факт пользователь будет видеть его полное определение, он не будет в состоянии использовать его, ни использовать закрытые методы. Таким образом, Вы обычно будете в состоянии изменить перечисление и содержание существующих методов, не нуждаясь в перекомпиляции кода с помощью класса.
Мое предположение было бы любой решением 3 или 1.
Если Вы действительно не хотите, чтобы Ваше перечисление появилось в Вашем заголовочном файле И гарантировало, что только используется закрытыми методами, то одно решение может состоять в том, чтобы пойти с pimpl принципом.
Это - техника, которые удостоверяются для сокрытия внутренностей класса в заголовках, просто объявив:
class A
{
public:
...
private:
void* pImpl;
};
Тогда в Вашем файле реализации (cpp), Вы объявляете класс, который будет представлением внутренностей.
class AImpl
{
public:
AImpl(A* pThis): m_pThis(pThis) {}
... all private methods here ...
private:
A* m_pThis;
};
необходимо динамично создать реализацию в конструкторе класса и удалить его в деструкторе и при реализации открытого метода, необходимо использовать:
((AImpl*)pImpl)->PrivateMethod();
существуют профессионалы для использования pimpl, каждый - то, что оно разъединяет Ваш заголовок класса от своей реализации, никакая потребность перекомпилировать другие классы при изменении одной реализации класса. Другой, это, ускоряет Ваше время компиляции, потому что Ваши заголовки так просты.
, Но это - боль для использования, таким образом, необходимо действительно спросить себя, просто объявление Вашего перечисления как частное в заголовке является так очень проблемой.
Я сделал бы это этот путь:
[в общедоступном заголовке]
typedef unsigned long E;
void Foo(E e);
[во внутреннем заголовке]
enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
FORCE_32BIT = 0xFFFFFFFF };
Путем добавления FORCE_32BIT мы удостоверяемся, что Econtent компилирует в длинное, таким образом, это является взаимозаменяемым E.
[Мой ответ является неправильным, но я оставил его здесь, потому что комментарии полезны].
Вперед перечисления объявления нестандартны, потому что указатели на различные перечислимые типы, как гарантируют, не будут тем же размером. Компилятор, возможно, должен видеть определение для знания, какие указатели размера могут использоваться с этим типом.
На практике, по крайней мере, на всех популярных компиляторах, указатели на перечисления являются последовательным размером. Предописание перечислений предоставлено как расширение языка Visual C++, например.
Нет действительно никакой такой вещи как предописание перечисления. Поскольку определение перечисления не содержит кода, который мог зависеть от другого кода с помощью перечисления, это - обычно не проблема определить перечисление полностью, когда Вы сначала объявляете его.
, Если единственное использование Вашего перечисления функциями члена парламента, не занимающего официального поста, можно реализовать инкапсуляцию при наличии самого перечисления как член парламента, не занимающий официального поста того класса. Перечисление все еще должно быть полностью определено при объявлении, то есть, в рамках определения класса. Однако это не большая проблема как объявление функций члена парламента, не занимающего официального поста там и не является худшим exposal внутренностей реализации, чем это.
при необходимости в более глубокой степени укрывательства для деталей реализации можно повредить его в абстрактный интерфейс, только состоящий из чистых виртуальных функций и бетона, полностью скрытого, реализация класса (наследовавшая) интерфейс. Создание экземпляров класса может быть обработано фабрикой или статической функцией членства интерфейса. Тот путь, даже реальное имя класса, уже не говоря о его закрытых функциях, не будет представлен.
Кажется, что это не может быть вперед - объявлено в GCC!
Интересное обсуждение здесь
Просто отметив, что причина на самом деле состоит в том, что размер перечисления еще не известен после предописания. Ну, Вы используете предописание структуры, чтобы смочь раздать указатель или относиться к объекту от места, в которое это отнесено в самом вперед заявленном определении структуры также.
Передайте объявление, что перечисление не было бы слишком полезно, потому что можно было бы хотеть смочь раздать перечисление значением. У Вас не могло даже быть указателя на него, потому что мне недавно сказали, что некоторые платформы используют указатели другого размера для символа, чем для интервала или долго. Таким образом, все это зависит от содержания перечисления.
Текущий Стандарт C++ явно запрещает выполнение чего-то как
enum X;
(в 7.1.5.3/1
). Но следующий Стандарт C++ из-за следующего года позволяет следующее, которое убедило меня, что проблема на самом деле имеет отношение к базовому типу:
enum X : int;
Это известно как "непрозрачное" перечислимое объявление. Можно даже использовать X значением в следующем коде. И его перечислители могут позже быть определены в более позднем redeclaration перечисления. Посмотрите 7.2
в текущем рабочем проекте.
Существует некоторое инакомыслие, так как это было ударено (вид), таким образом, вот некоторые соответствующие биты из стандарта. Исследование показывает, что стандарт действительно не определяет предописание, и при этом это явно не указывает, что перечисления могут или не могут быть вперед объявлены.
Во-первых, от dcl.enum, разделите 7.2:
Базовый тип перечисления является целочисленным типом, который может представить все значения перечислителя, определенные в перечислении. Это определяется реализацией, какой целочисленный тип используется в качестве базового типа для перечисления за исключением того, что базовый тип не должен быть больше, чем интервал, если значение перечислителя не сможет поместиться в международный или неподписанный интервал. Если список перечислителя пуст, базовый тип - то, как будто перечисление имело единственный перечислитель со значением 0. Значение sizeof () относилось к перечисляемому типу, объект перечисляемого типа или перечислитель, является значением sizeof (), относился к базовому типу.
Таким образом, базовый тип перечисления определяется реализацией с одним незначительным ограничением.
Затем мы зеркально отражаем к разделу по "неполным типам" (3.9), который является почти настолько близко, как мы приходим к любому стандарту на предописаниях:
Класс, который был объявлен, но не определен, или массив неизвестного размера или неполного типа элемента, является не полностью определенным типом объекта.
Тип класса (такой как "класс X") мог бы быть неполным однажды в единице перевода и завершенным позже; тип "класс X" является тем же типом в обеих точках. Заявленный тип объекта массива мог бы быть массивом неполного типа класса и поэтому неполный; если тип класса завершается позже в единице перевода, тип массива становится завершенным; тип массива в тех двух точках является тем же типом. Заявленный тип объекта массива мог бы быть массивом неизвестного размера и поэтому быть неполным однажды в единице перевода и быть завершенным позже; типы массива в тех двух точках ("массив неизвестных, связанных T" и "массива N T"), являются различными типами. Тип указателя на массив неизвестного размера, или типа, определенного объявлением определения типа, чтобы быть массивом неизвестного размера, не может быть завершен.
Таким образом, там, стандарт в значительной степени разметил типы, которые могут быть вперед объявлены. Перечисление не было там, таким образом, авторы компилятора обычно расценивают вперед объявление, как запрещено стандартом из-за переменного размера его базового типа.
Это имеет смысл, также. На перечисления обычно ссылаются в ситуациях значением, и компилятор должен был бы действительно знать размер ресурса хранения в тех ситуациях. Так как размер ресурса хранения является определенной реализацией, много компиляторов могут просто принять решение использовать 32 битовых значения для базового типа каждого перечисления, в которой точке становится возможно передать, объявляют их. Интересный эксперимент мог бы быть должен попытаться вперед объявить перечисление в Visual Studio, затем вынудив это использовать базовый тип, больше, чем sizeof (интервал), как объяснено выше для наблюдения то, что происходит.
Для VC вот тест о предописании и указывающий лежащий в основе типа:
typedef int myint; enum T ; void foo(T * tp ) { * tp = (T)0x12345678; } enum T : char { A };
Но добрался, предупреждение для/W4 (/W3 не подвергаются этому предупреждению),
предупреждение C4480: нестандартное расширение использовало: определение базового типа для перечислимого 'T'
VC (Microsoft(R) 32-разрядная Версия 15.00.30729.01 Оптимизирующего компилятора C/C++ для 80x86) смотрит багги в вышеупомянутом случае:
?foo@@YAXPAW4T@@@Z PROC ; foo ; File e:\work\c_cpp\cpp_snippet.cpp ; Line 13 push ebp mov ebp, esp ; Line 14 mov eax, DWORD PTR _tp$[ebp] mov DWORD PTR [eax], 305419896 ; 12345678H ; Line 15 pop ebp ret 0 ?foo@@YAXPAW4T@@@Z ENDP ; foo
Вышеупомянутый ассемблерный код извлечен из/Fatest.asm непосредственно, не моего персонального предположения. Сделайте Вы видите mov DWORD PTR [eax], 305419896; 12345678-я строка?
следующий фрагмент кода доказывает его:
int main(int argc, char *argv) { union { char ca[4]; T t; }a; a.ca[0] = a.ca[1] = a.[ca[2] = a.ca[3] = 1; foo( &a.t) ; printf("%#x, %#x, %#x, %#x\n", a.ca[0], a.ca[1], a.ca[2], a.ca[3] ); return 0; }
результат: 0x78, 0x56, 0x34, 0x12
вышеупомянутая ключевая инструкция становится:
БАЙТ mov PTR [eax], 120; 00000078-й
конечный результат: 0x78, 0x1, 0x1, 0x1
Обратите внимание, что значение не перезаписывается
Так использование предописания перечисления в VC считают вредным.
BTW, для не удивления синтаксис для объявления базового типа - то же как в C#. В практике я нашел, что стоит для сохранения 3 байтов путем определения базового типа как символа, когда говорят со встроенной системой, которая является ограниченной памятью.
В своих проектах я использовал технику Namespace-Bound Enumeration для работы с enum
из устаревших и сторонних компонентов. Вот пример:
namespace type
{
class legacy_type;
typedef const legacy_type& type;
}
// May be defined here or pulled in via #include.
namespace legacy
{
enum evil { x , y, z };
}
namespace type
{
using legacy::evil;
class legacy_type
{
public:
legacy_type(evil e)
: e_(e)
{}
operator evil() const
{
return e_;
}
private:
evil e_;
};
}
#include "forward.h"
class foo
{
public:
void f(type::type t);
};
#include "foo.h"
#include <iostream>
#include "enum.h"
void foo::f(type::type t)
{
switch (t)
{
case legacy::x:
std::cout << "x" << std::endl;
break;
case legacy::y:
std::cout << "y" << std::endl;
break;
case legacy::z:
std::cout << "z" << std::endl;
break;
default:
std::cout << "default" << std::endl;
}
}
#include "foo.h"
#include "enum.h"
int main()
{
foo fu;
fu.f(legacy::x);
return 0;
}
Обратите внимание, что foo Заголовок .h
не должен ничего знать о legacy :: evil
. Только файлы, которые используют устаревший тип legacy :: evil
(здесь: main.cc), должны включать enum.h
.
Прямое объявление перечислений также возможно в C ++ 0x. Ранее причина, по которой типы перечислений не могли быть объявлены вперед, заключалась в том, что размер перечисления зависел от его содержимого. Если размер перечисления определяется приложением, его можно объявить вперед:
enum Enum1; //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int; //Legal in C++0x.
enum class Enum3; //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short; //Illegal in C++0x, because Enum2 was previously declared with a different type.
Прямое объявление вещей в C ++ очень полезно, потому что оно значительно ускоряет время компиляции . Вы можете переадресовать объявление нескольких вещей в C ++, включая: struct
, class
, function
и т. Д.
Но можете ли вы переслать объявление enum
в C ++?
Нет, не можете.
Но почему бы не допустить этого? Если бы это было разрешено, вы могли бы определить свой тип enum
в заголовочном файле и значения enum
в исходном файле. Похоже, это должно быть разрешено, верно?
Неправильно.
В C ++ нет типа по умолчанию для enum
, как в C # (int). В C ++ ваш тип enum
будет определен компилятором как любой тип, который будет соответствовать диапазону значений, который у вас есть для вашего enum
.
Что это значит?
Это означает, что базовый тип вашего перечисления
не может быть полностью определен, пока не будут определены все значения перечисления
. Каких именно, вы не можете разделить декларацию и определение вашего перечисления
. И поэтому вы не можете пересылать объявление enum
в C ++.
Стандарт ISO C ++ S7.2.5:
Базовый тип перечисления является интегральным типом, который может представлять все значения перечислителя, определенные в перечисление. Это определяется реализацией, какой интегральный тип используется в качестве базового типа для перечисления, за исключением того, что базовый тип не должен быть больше, чем
int
, если только значение перечислителя не может поместиться вint
] илицелое число без знака
. Если список-перечислитель пуст, базовый тип выглядит так, как если бы в перечислении был один перечислитель со значением 0. Значениеsizeof ()
, примененное к типу перечисления, объекту типа перечисления или объекту enumerator - это значениеsizeof ()
, примененное к базовому типу.
Вы можете определить размер перечислимого типа в C ++ с помощью оператора sizeof
. Размер перечисляемого типа - это размер его базового типа. Таким образом вы можете угадать, какой тип ваш компилятор использует для вашего перечисления
.
Что если вы укажете тип своего перечисления
явно следующим образом:
enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);
Можете ли вы затем пересылать объявление своего перечисления
?
Нет. Но почему бы и нет?
Указание типа перечисления
на самом деле не является частью текущего стандарта C ++. Это расширение VC ++. Однако это будет частью C ++ 0x.