2009-03-13 8 views

ответ

42

Не возможно. Наследования с перечислениями нет.

Вместо этого вы можете использовать классы с named const ints.

Пример:

class Colors 
{ 
public: 
    static const int RED = 1; 
    static const int GREEN = 2; 
}; 

class RGB : public Colors 
{ 
    static const int BLUE = 10; 
}; 


class FourColors : public Colors 
{ 
public: 
    static const int ORANGE = 100; 
    static const int PURPLE = 101; 
}; 
+0

Есть проблемы с этим решением? Например, (у меня нет глубокого понимания полиморфизма). Можно ли иметь вектор и использовать, p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;? – jespestana

+1

@jespestana Нет, вы не будете использовать экземпляры класса «Цвета». Вы используете только значения int в статических константных членах. – jiandingzhe

+0

Если я правильно понимаю вас; тогда мне придется использовать векторный контейнер . Но я все равно буду писать: p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;. правильно? – jespestana

75
#include <iostream> 
#include <ostream> 

class Enum 
{ 
public: 
    enum 
    { 
     One = 1, 
     Two, 
     Last 
    }; 
}; 

class EnumDeriv : public Enum 
{ 
public: 
    enum 
    { 
     Three = Enum::Last, 
     Four, 
     Five 
    }; 
}; 

int main() 
{ 
    std::cout << EnumDeriv::One << std::endl; 
    std::cout << EnumDeriv::Four << std::endl; 
    return 0; 
} 
+1

Я в замешательстве! Как бы вы тогда ссылались на типы Enum в аргументе переменной или функции, и как бы вы гарантировали, что функция, ожидающая, что Enum не получит EnumDeriv? –

+12

Это не сработает. Когда вы определяете некоторые функции 'int basic (EnumBase b) {return b; } 'и' int производный (EnumDeriv d) {return d; } ', эти типы не будут конвертируемыми в' int', хотя обычные перечисления. И когда вы пробуете даже такой простой код, как этот: 'cout << basic (EnumBase :: One) << endl;', тогда вы получите сообщение об ошибке: 'conversion from 'EnumBase :: ' to нескалярный тип «EnumBase» запрошен'. Возможно, эти проблемы могут быть преодолены путем добавления некоторых операторов преобразования. – SasQ

0

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

7

Вы не можете сделать это напрямую, но можете попробовать использовать решение из статьи this.

Основная идея состоит в том, чтобы использовать класс-хелпер-шаблон, который содержит значения перечисления и имеет оператор типа cast. Учитывая, что базовый тип для перечисления - int, вы можете легко использовать этот класс владельца в своем коде вместо перечисления.

+0

Хотя этот фрагмент кода может решить вопрос, [включая объяснение] (http://meta.stackexchange.com/questions/114762/explaining-entirely- code-based-answers) действительно помогает улучшить качество ваших после. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода. – NathanOliver

+0

Это отличный ответ; это один из тех случаев, когда «думать о проблеме по-другому», и идея использования шаблона действительно соответствует законопроекту. – JasonDiplomat

+0

Также рассмотрим некоторые из решений этих шаблонов vis-a-vis: https://stackoverflow.com/questions/5871722/how-to-achieve-virtual-template-function-in-c – JasonDiplomat

1

Ну, если вы определите enum с тем же именем в производном классе и запустите его из последнего элемента корреспонденции enum в базовом классе, вы получите почти то, что хотите - унаследованное перечисление. Посмотрите на этот код:

class Base 
{ 
public: 
enum ErrorType 
    { 
    GeneralError, 
    NoMemory, 
    FileNotFound, 
    LastItem 
    } 
} 

class Inherited: public Base 
{ 
enum ErrorType 
    { 
    SocketError = Base::LastItem, 
    NotEnoughBandwidth, 
    } 
} 
+0

, в то время как код компилируется, вы не сможете его использовать, поскольку компилятор не сможет конвертировать из base :: ErrorType в Inherited :: ErrorType. – bavaza

+0

@bavaza, конечно, вы должны использовать целое число вместо перечислений при передаче их значений в качестве параметров. – Haspemulator

2

Как заявил bayda, ENUM-х нет (и/или не должны) иметь функциональные возможности, поэтому я принял следующий подход к вашему затруднительном положении путем адаптации Mykola Golubyev «s ответ :

typedef struct 
{ 
    enum 
    { 
     ONE = 1, 
     TWO, 
     LAST 
    }; 
}BaseEnum; 

typedef struct : public BaseEnum 
{ 
    enum 
    { 
     THREE = BaseEnum::LAST, 
     FOUR, 
     FIVE 
    }; 
}DerivedEnum; 
+2

Существует несколько проблем с этим решением. Во-первых, вы загрязняете BaseEnum LAST, который на самом деле не существует, кроме как установить начальную точку для DerivedEnum. Во-вторых, что, если я хочу явно установить некоторые значения в BaseEnum, которые будут сталкиваться с значениями DerivedEnum? В любом случае, это, наверное, самое лучшее, что мы можем сделать до сих пор, как в C++ 14. –

+0

Как я уже сказал, он адаптирован из предыдущего примера, поэтому он выражается так, как он есть для полноты, это не мое беспокойство, что предыдущий плакат имеет логические проблемы в своем примере – vigilance

4

К сожалению, это невозможно в C++ 14. Надеюсь, у нас будет такая языковая функция в C++ 17. Поскольку у вас уже мало обходных решений для вашей проблемы, я не буду предлагать решение.

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

+0

Обратите внимание, что 'extends' является ключевым словом для наследования в Эйфелевой язык. –

+0

Вы правы, потому что в этом случае Принцип замещения Лискова не соблюдается. Из-за этого comitee не будет принимать решение, похожее на наследование синтаксически. –

3

Как насчет этого? Ок, экземпляр создается для каждого возможного значения, но, кроме того, он очень гибкий. Есть ли недостатки?

.h:

class BaseEnum 
{ 
public: 
    static const BaseEnum ONE; 
    static const BaseEnum TWO; 

    bool operator==(const BaseEnum& other); 

protected: 
    BaseEnum() : i(maxI++) {} 
    const int i; 
    static int maxI; 
}; 

class DerivedEnum : public BaseEnum 
{ 
public: 
    static const DerivedEnum THREE; 
}; 

.каст:

int BaseEnum::maxI = 0; 

bool BaseEnum::operator==(const BaseEnum& other) { 
    return i == other.i; 
} 

const BaseEnum BaseEnum::ONE; 
const BaseEnum BaseEnum::TWO; 
const DerivedEnum DerivedEnum::THREE; 

Использование:

BaseEnum e = DerivedEnum::THREE; 

if (e == DerivedEnum::THREE) { 
    std::cerr << "equal" << std::endl; 
} 
+0

Единственные недостатки, которые я вижу, - это более высокая потребляемая память, и для этого требуется больше строк кода. Но я попробую ваше решение. – Knitschi

+0

Я также сделал '' '' '' BaseEnum :: i'''' public и '' '' BaseEnum :: maxI'''' частным. – Knitschi

+0

Защищенный конструктор по умолчанию может быть проблемой, когда перечисление должно использоваться в сторонних макросах или шаблонах, для которых требуется конструктор по умолчанию. – Knitschi

0

Вы можете использовать проект SuperEnum для создания выдвижных перечислений.

/*** my_enum.h ***/ 
class MyEnum: public SuperEnum<MyEnum> 
{ 
public: 
    MyEnum() {} 
    explicit MyEnum(const int &value): SuperEnum(value) {} 

    static const MyEnum element1; 
    static const MyEnum element2; 
    static const MyEnum element3; 
}; 

/*** my_enum.cpp ***/ 
const MyEnum MyEnum::element1(1); 
const MyEnum MyEnum::element2; 
const MyEnum MyEnum::element3; 

/*** my_enum2.h ***/ 
class MyEnum2: public MyEnum 
{ 
public: 
    MyEnum2() {} 
    explicit MyEnum2(const int &value): MyEnum(value) {} 

    static const MyEnum2 element4; 
    static const MyEnum2 element5; 
}; 

/*** my_enum2.cpp ***/ 
const MyEnum2 MyEnum2::element4; 
const MyEnum2 MyEnum2::element5; 

/*** main.cpp ***/ 
std::cout << MyEnum2::element3; 
// Output: 3 
Смежные вопросы