Передайте объявление перечисления в C++

Другое событие 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));
}

257
задан Cœur 18 December 2018 в 01:46
поделиться

15 ответов

Причина перечисление не может быть вперед объявлено, состоит в том, что, не зная значения, компилятор не может знать устройство хранения данных, требуемое для перечислимой переменной. Компилятору 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

209
ответ дан KJAWolf 23 November 2019 в 02:43
поделиться

Вы определяете перечисление для ограничения возможных значений элементов типа к ограниченному набору. Это ограничение должно быть осуществлено во время компиляции.

, вперед объявление того, что Вы будете использовать 'ограниченный набор' позже, не добавит значения: последующий код должен знать возможные значения для пользы из него.

, Хотя компилятор касавшийся размера перечислимого типа, , намерение из перечисления теряется, когда Вы вперед объявляете это.

-1
ответ дан xtofl 23 November 2019 в 02:43
поделиться

Поскольку перечисление может быть интегральным размером переменного размера (компилятор решает, которые измеряют данное перечисление, имеет), указатель на перечисление может также иметь переменный размер, так как это - целочисленный тип (символы имеют указатели различного размера на некоторых платформах, например).

, Таким образом, компилятор не может даже позволить, Вы передать - объявляете перечисление и пользователя указатель на него, потому что даже там, ему нужен размер перечисления.

-1
ответ дан Carl Seleborg 23 November 2019 в 02:43
поделиться

Мое решение Вашей проблемы было бы к также:

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.

0
ответ дан paercebal 23 November 2019 в 02:43
поделиться

Если Вы действительно не хотите, чтобы Ваше перечисление появилось в Вашем заголовочном файле И гарантировало, что только используется закрытыми методами, то одно решение может состоять в том, чтобы пойти с 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, каждый - то, что оно разъединяет Ваш заголовок класса от своей реализации, никакая потребность перекомпилировать другие классы при изменении одной реализации класса. Другой, это, ускоряет Ваше время компиляции, потому что Ваши заголовки так просты.

, Но это - боль для использования, таким образом, необходимо действительно спросить себя, просто объявление Вашего перечисления как частное в заголовке является так очень проблемой.

2
ответ дан Vincent Robert 23 November 2019 в 02:43
поделиться

Я сделал бы это этот путь:

[в общедоступном заголовке]

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.

4
ответ дан Laurie Cheers 23 November 2019 в 02:43
поделиться

[Мой ответ является неправильным, но я оставил его здесь, потому что комментарии полезны].

Вперед перечисления объявления нестандартны, потому что указатели на различные перечислимые типы, как гарантируют, не будут тем же размером. Компилятор, возможно, должен видеть определение для знания, какие указатели размера могут использоваться с этим типом.

На практике, по крайней мере, на всех популярных компиляторах, указатели на перечисления являются последовательным размером. Предописание перечислений предоставлено как расширение языка Visual C++, например.

14
ответ дан James Hopkin 23 November 2019 в 02:43
поделиться

Нет действительно никакой такой вещи как предописание перечисления. Поскольку определение перечисления не содержит кода, который мог зависеть от другого кода с помощью перечисления, это - обычно не проблема определить перечисление полностью, когда Вы сначала объявляете его.

, Если единственное использование Вашего перечисления функциями члена парламента, не занимающего официального поста, можно реализовать инкапсуляцию при наличии самого перечисления как член парламента, не занимающий официального поста того класса. Перечисление все еще должно быть полностью определено при объявлении, то есть, в рамках определения класса. Однако это не большая проблема как объявление функций члена парламента, не занимающего официального поста там и не является худшим exposal внутренностей реализации, чем это.

при необходимости в более глубокой степени укрывательства для деталей реализации можно повредить его в абстрактный интерфейс, только состоящий из чистых виртуальных функций и бетона, полностью скрытого, реализация класса (наследовавшая) интерфейс. Создание экземпляров класса может быть обработано фабрикой или статической функцией членства интерфейса. Тот путь, даже реальное имя класса, уже не говоря о его закрытых функциях, не будет представлен.

7
ответ дан Alexey Feldgendler 23 November 2019 в 02:43
поделиться

Кажется, что это не может быть вперед - объявлено в GCC!

Интересное обсуждение здесь

2
ответ дан prakash 23 November 2019 в 02:43
поделиться

Просто отметив, что причина на самом деле состоит в том, что размер перечисления еще не известен после предописания. Ну, Вы используете предописание структуры, чтобы смочь раздать указатель или относиться к объекту от места, в которое это отнесено в самом вперед заявленном определении структуры также.

Передайте объявление, что перечисление не было бы слишком полезно, потому что можно было бы хотеть смочь раздать перечисление значением. У Вас не могло даже быть указателя на него, потому что мне недавно сказали, что некоторые платформы используют указатели другого размера для символа, чем для интервала или долго. Таким образом, все это зависит от содержания перечисления.

Текущий Стандарт C++ явно запрещает выполнение чего-то как

enum X;

7.1.5.3/1). Но следующий Стандарт C++ из-за следующего года позволяет следующее, которое убедило меня, что проблема на самом деле имеет отношение к базовому типу:

enum X : int;

Это известно как "непрозрачное" перечислимое объявление. Можно даже использовать X значением в следующем коде. И его перечислители могут позже быть определены в более позднем redeclaration перечисления. Посмотрите 7.2 в текущем рабочем проекте.

5
ответ дан Johannes Schaub - litb 23 November 2019 в 02:43
поделиться

Существует некоторое инакомыслие, так как это было ударено (вид), таким образом, вот некоторые соответствующие биты из стандарта. Исследование показывает, что стандарт действительно не определяет предописание, и при этом это явно не указывает, что перечисления могут или не могут быть вперед объявлены.

Во-первых, от dcl.enum, разделите 7.2:

Базовый тип перечисления является целочисленным типом, который может представить все значения перечислителя, определенные в перечислении. Это определяется реализацией, какой целочисленный тип используется в качестве базового типа для перечисления за исключением того, что базовый тип не должен быть больше, чем интервал, если значение перечислителя не сможет поместиться в международный или неподписанный интервал. Если список перечислителя пуст, базовый тип - то, как будто перечисление имело единственный перечислитель со значением 0. Значение sizeof () относилось к перечисляемому типу, объект перечисляемого типа или перечислитель, является значением sizeof (), относился к базовому типу.

Таким образом, базовый тип перечисления определяется реализацией с одним незначительным ограничением.

Затем мы зеркально отражаем к разделу по "неполным типам" (3.9), который является почти настолько близко, как мы приходим к любому стандарту на предописаниях:

Класс, который был объявлен, но не определен, или массив неизвестного размера или неполного типа элемента, является не полностью определенным типом объекта.

Тип класса (такой как "класс X") мог бы быть неполным однажды в единице перевода и завершенным позже; тип "класс X" является тем же типом в обеих точках. Заявленный тип объекта массива мог бы быть массивом неполного типа класса и поэтому неполный; если тип класса завершается позже в единице перевода, тип массива становится завершенным; тип массива в тех двух точках является тем же типом. Заявленный тип объекта массива мог бы быть массивом неизвестного размера и поэтому быть неполным однажды в единице перевода и быть завершенным позже; типы массива в тех двух точках ("массив неизвестных, связанных T" и "массива N T"), являются различными типами. Тип указателя на массив неизвестного размера, или типа, определенного объявлением определения типа, чтобы быть массивом неизвестного размера, не может быть завершен.

Таким образом, там, стандарт в значительной степени разметил типы, которые могут быть вперед объявлены. Перечисление не было там, таким образом, авторы компилятора обычно расценивают вперед объявление, как запрещено стандартом из-за переменного размера его базового типа.

Это имеет смысл, также. На перечисления обычно ссылаются в ситуациях значением, и компилятор должен был бы действительно знать размер ресурса хранения в тех ситуациях. Так как размер ресурса хранения является определенной реализацией, много компиляторов могут просто принять решение использовать 32 битовых значения для базового типа каждого перечисления, в которой точке становится возможно передать, объявляют их. Интересный эксперимент мог бы быть должен попытаться вперед объявить перечисление в Visual Studio, затем вынудив это использовать базовый тип, больше, чем sizeof (интервал), как объяснено выше для наблюдения то, что происходит.

1
ответ дан Dan Olson 23 November 2019 в 02:43
поделиться

Для VC вот тест о предописании и указывающий лежащий в основе типа:

  1. следующий код компилируется хорошо.
    typedef int myint;
    enum T ;
    void foo(T * tp )
    {
        * tp = (T)0x12345678;
    }
    enum T : char
    {
        A
    };

Но добрался, предупреждение для/W4 (/W3 не подвергаются этому предупреждению),

предупреждение C4480: нестандартное расширение использовало: определение базового типа для перечислимого 'T'

  1. VC (Microsoft(R) 32-разрядная Версия 15.00.30729.01 Оптимизирующего компилятора C/C++ для 80x86) смотрит багги в вышеупомянутом случае:

    • при наблюдении перечисления T; VC принимает перечислимый интервал 4 байтов значения по умолчанию использования типа T как лежащий в основе типа, таким образом, сгенерированный ассемблерный код:
    ?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

  • после удаляют предописание перечисления T и перемещают определение функционального нечто после перечислимого определения T: результат в порядке:

вышеупомянутая ключевая инструкция становится:

БАЙТ mov PTR [eax], 120; 00000078-й

конечный результат: 0x78, 0x1, 0x1, 0x1

Обратите внимание, что значение не перезаписывается

Так использование предописания перечисления в VC считают вредным.

BTW, для не удивления синтаксис для объявления базового типа - то же как в C#. В практике я нашел, что стоит для сохранения 3 байтов путем определения базового типа как символа, когда говорят со встроенной системой, которая является ограниченной памятью.

1
ответ дан zhaorufei 23 November 2019 в 02:43
поделиться

В своих проектах я использовал технику Namespace-Bound Enumeration для работы с enum из устаревших и сторонних компонентов. Вот пример:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// 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_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#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;
    }
}

main.cc:

#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 .

1
ответ дан 23 November 2019 в 02:43
поделиться

Прямое объявление перечислений также возможно в 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.
192
ответ дан 23 November 2019 в 02:43
поделиться

Прямое объявление вещей в 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.

Источник

30
ответ дан 23 November 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: