Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.
Например, ниже - класс ученика, который будет использовать его в нашем коде.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
Приведенный ниже код дает вам исключение с нулевым указателем.
public class School {
Student obj_Student;
public School() {
try {
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
Поскольку вы используете Obj_Student
, но вы забыли инициализировать его, как в правильном коде, показанном ниже:
public class School {
Student obj_Student;
public School() {
try {
obj_Student = new Student();
obj_Student.setId(12);
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
enum
определяет синтаксический элемент.
#define
предварительная директива препроцессору, выполняемая прежде , компилятор видит код, и поэтому не является элементом языка самого C.
Обычно перечисления предпочтены, поскольку они безопасные с точки зрения типов и более легко поддающимися обнаружению. Определяет более тверды расположиться и может иметь сложное поведение, например, одна часть кода может переопределить #define
сделанный другим. Это может быть трудно разыскать.
Другое преимущество перечисления по списку определяет, то, что компиляторы (gcc, по крайней мере) могут генерировать предупреждение, что если не все значения проверяются в операторе переключения. Например:
enum {
STATE_ONE,
STATE_TWO,
STATE_THREE
};
...
switch (state) {
case STATE_ONE:
handle_state_one();
break;
case STATE_TWO:
handle_state_two();
break;
};
В предыдущем коде, компилятор в состоянии генерировать предупреждение, что не все значения перечисления обрабатываются в переключателе. Если бы состояния были сделаны как #define, это не имело бы место.
В дополнение к упомянутым выше положительным сторонам можно ограничить объем перечислений к классу, структуре или пространству имен. Лично, мне нравится иметь минимальное количество соответствующих символов в объеме в любой момент, который является другой причиной использования перечислений, а не #defines.
Если бы у Вас есть группа констант (как "Дни Недели"), перечисления были бы предпочтительны, потому что она показывает, что они сгруппированы; и, как Jason сказал, они безопасны с точки зрения типов. Если бы это - глобальная константа (как номер версии), это больше, для чего Вы использовали бы #define
; хотя это - предмет большого количества дебатов.
Перечисления обычно предпочитаются по #define везде, где имеет смысл использовать перечисление:
enum
значение с (" openType: OpenExisting
", а не" openType: 2
", #define
ition. самое большое различие - то, что можно использовать перечисления в качестве типов:
// Yeah, dumb example
enum OpenType {
OpenExisting,
OpenOrCreate,
Truncate
};
void OpenFile(const char* filename, OpenType openType, int bufferSize);
Это дает Вам проверку типа параметров (Вы не можете перепутать openType и bufferSize как легко), и облегчает находить то, что значения допустимы, делая Ваши интерфейсы намного легче использовать. Некоторые IDE могут даже дать Вам <забастовка> intellisense забастовка> завершение кода!
Определите команда препроцессора, она точно так же, как делает, "заменяют все" в Вашем редакторе, она может заменить строку другим и затем скомпилировать результат.
Перечисление является особым случаем типа, например, если Вы пишете:
enum ERROR_TYPES
{
REGULAR_ERR =1,
OK =0
}
там существует новый тип под названием ERROR_TYPES. Это верно, что REGULAR_ERR уступает 1, но бросающий от этого типа до интервала должен произвести кастинг, предупреждающий (если Вы настраиваете свой компилятор к высокому многословию).
Сводка: они - оба, но при использовании перечисления Вы получаете прибыль, проверка типа и при помощи определяет Вас, просто заменяют строки кода.
#define
операторы обрабатываются препроцессором, прежде чем компилятор доберется для наблюдения кода, таким образом, это - в основном текстовая замена (это на самом деле немного более интеллектуально с использованием параметров и такого).
Перечисления являются частью самого языка C и имеют следующие преимущества.
1/у Них может быть тип, и компилятор может проверка типа их.
2/, Так как они доступны компилятору, информации о символе о них, может быть передан до отладчика, делая отладку легче.
Для целочисленных значений констант я предпочитаю enum
перед #define
. Кажется, что нет недостатков в использовании enum
(не считая крохотного недостатка, связанного с большим объемом ввода), но у вас есть то преимущество, что enum
может быть ограничено, а #define
идентификаторы имеют глобальную область видимости, которая мешает всему.
Использование #define
обычно не проблема, но поскольку у enum
нет недостатков, я согласен с этим.
В C ++ я также обычно предпочитаю enum
const int
, хотя в C ++ a const int
может использоваться вместо буквального целочисленного значения (в отличие от в C), потому что enum
переносится на C (с которым я все еще много работаю).
Помимо всего уже написанного, одно сказано, но не показано, а вместо этого интересно. Например.
enum action { DO_JUMP, DO_TURNL, DO_TURNR, DO_STOP };
//...
void do_action( enum action anAction, info_t x );
Рассмотрение действия как типа проясняет ситуацию. Используя define, вы должны написать
void do_action(int anAction, info_t x);
перечисления больше используются для перечисления некоторого набора, например, дней в неделе. Если вам нужно только одно постоянное число, const int
(или double и т. Д.) Определенно будет лучше, чем enum. Мне лично не нравится #define
(по крайней мере, не для определения некоторых констант), потому что он не дает мне безопасности типов, но вы, конечно, можете использовать его, если он вам больше подходит.
Если вам нужна только эта единственная константа (скажем, для размера буфера), я бы использовал не перечисление, а определение. Я бы использовал перечисления для таких вещей, как возвращаемые значения (которые означают разные условия ошибки) и везде, где нам нужно различать разные «типы» или «случаи». В этом случае мы можем использовать перечисление для создания нового типа, который мы можем использовать в прототипах функций и т. Д., И тогда компилятор сможет лучше проверить этот код на работоспособность.
По возможности всегда лучше использовать перечисление. Использование перечисления дает компилятору больше информации о вашем исходном коде, определение препроцессора никогда не просматривается компилятором и, следовательно, несет меньше информации.
Для реализации, например, набор режимов, использование перечисления позволяет компилятору отлавливать, например, отсутствующие case
-выражения в переключателе.
Создание перечисления создает не только литералы, но и тип, который группирует эти литералы: Это добавляет семантику в ваш код, которую компилятор может проверить.
Более того, при использовании отладчика вы имеете доступ к значениям литералов перечисления. В случае с #define это не всегда так.
#define - это команда препроцессора, enum - в языке C или C++.
Для таких случаев всегда лучше использовать enum, а не #define. Одно дело - безопасность типов. Другой - когда у вас есть последовательность значений, вам нужно указать в перечислении только начало последовательности, остальные значения получают последовательные значения.
enum {
ONE = 1,
TWO,
THREE,
FOUR
};
вместо
#define ONE 1
#define TWO 2
#define THREE 3
#define FOUR 4
В качестве примечания, есть еще некоторые случаи, когда вы можете использовать #define (обычно для некоторых видов макросов, если вам нужно иметь возможность построить идентификатор, который содержит константу), но это своего рода черная магия макросов, и очень очень редко, чтобы быть способом пойти. Если вы идете на такие крайности, вам, вероятно, следует использовать шаблон C++ (но если вы застряли с C...).
enum может группировать несколько элементов в одну категорию:
enum fruits{ apple=1234, orange=12345};
в то время как #define может создавать только несвязанные константы:
#define apple 1234
#define orange 12345