Kế thừa lớp enum cơ sở


79

Có một mẫu mà tôi có thể kế thừa enum từ một enum khác trong C ++ không?

Đại loại vậy:

enum eBase 
{
   one=1, two, three
};


enum eDerived: public eBase
{
   four=4, five, six
};

Câu trả lời:


67

Không thể. Không có thừa kế với enums.

Thay vào đó, bạn có thể sử dụng các lớp có tên int const.

Thí dụ:

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

Có vấn đề gì với giải pháp này không? Ví dụ: (Tôi không có hiểu biết sâu về Đa hình) Có thể có một vectơ <Màu> và sử dụng, p = std :: find (mycolors, mycolor + length, Colors :: ORANGE);?
jespestana

1
@jespestana Không, bạn sẽ không sử dụng Colorscác phiên bản lớp. Bạn chỉ sử dụng các giá trị int trong các thành viên const tĩnh.
jiandingzhe

Nếu tôi hiểu bạn đúng; sau đó, tôi sẽ phải sử dụng một vùng chứa vector <Int>. Nhưng tôi vẫn có thể viết: p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;. đúng?
jespestana

1
@jespestana hoàn toàn. ngoài ra, nếu tìm kiếm là một hoạt động rất phổ biến, hãy xem xét sử dụng bộ_bạn hoặc bộ băm địa chỉ mở.
v.oddou

1
Re: Có vấn đề gì với giải pháp này không? thể có vấn đề khi các giá trị này không còn thuộc loại riêng biệt nữa. Bạn không thể viết một hàm mong đợi một Color, giống như bạn có thể cho một enum.
Drew Dormann

93
#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
Tôi bối rối! Sau đó, bạn sẽ tham chiếu đến các kiểu Enum như thế nào trong đối số biến hoặc hàm, và làm thế nào bạn đảm bảo rằng một hàm mong đợi Enum không được cung cấp EnumDeriv?
Sideshow Bob

21
Điều này sẽ không hoạt động. Khi bạn xác định một số hàm int basic(EnumBase b) { return b; }int derived(EnumDeriv d) { return d; }, các kiểu đó sẽ không thể chuyển đổi thành int, mặc dù các enums đơn giản. Và khi bạn cố gắng mã đơn giản, ngay cả như vậy như thế này: cout << basic(EnumBase::One) << endl;, sau đó bạn sẽ nhận được một lỗi: conversion from ‘EnumBase::<anonymous enum>’ to non-scalar type ‘EnumBase’ requested. Những vấn đề đó có thể được giải quyết quá mức bằng cách thêm một số toán tử chuyển đổi.
SasQ

10

Bạn không thể làm điều đó trực tiếp, nhưng bạn có thể thử sử dụng giải pháp từ này bài viết.

Ý tưởng chính là sử dụng lớp mẫu trình trợ giúp chứa các giá trị enum và có toán tử ép kiểu. Xem xét rằng kiểu cơ bản cho enum là intbạn có thể sử dụng lớp chủ này liền mạch trong mã của mình thay vì enum.


Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
NathanOliver

Đây là một câu trả lời tuyệt vời; đó là một trong những trường hợp "nghĩ về vấn đề theo cách khác" và ý tưởng sử dụng một mẫu thực sự phù hợp với dự luật.
Den-Jason

Ngoài ra, hãy xem một số giải pháp cho các mẫu trực quan này: stackoverflow.com/questions/5871722/…
Den-Jason

5

Thật không may, nó không thể thực hiện được trong C ++ 14. Tôi hy vọng chúng ta sẽ có một tính năng ngôn ngữ như vậy trong C ++ 17. Vì bạn đã có một số giải pháp thay thế cho vấn đề của mình nên tôi sẽ không cung cấp giải pháp.

Tôi muốn chỉ ra rằng từ ngữ nên là "mở rộng" chứ không phải "kế thừa". Phần mở rộng cho phép nhiều giá trị hơn (vì bạn đang nhảy từ 3 đến 6 giá trị trong ví dụ của mình) trong khi kế thừa có nghĩa là đặt nhiều ràng buộc hơn vào một lớp cơ sở nhất định để tập hợp các khả năng thu hẹp lại. Do đó, đúc tiềm năng sẽ hoạt động hoàn toàn ngược lại với kế thừa. Bạn có thể ép kiểu lớp dẫn xuất đến lớp cơ sở chứ không phải lớp phó với kế thừa lớp. Nhưng khi có các phần mở rộng, bạn "nên" có thể truyền lớp cơ sở đến phần mở rộng của nó chứ không phải là phần mở rộng. Tôi đang nói "nên" bởi vì, như tôi đã nói, một tính năng ngôn ngữ như vậy vẫn chưa tồn tại.


Lưu ý rằng đó extendslà một từ khóa để thừa kế trong ngôn ngữ Eiffel.
Chúc mừng và hth. - Alf

Bạn đúng vì trong trường hợp này Nguyên tắc thay thế Liskov không được tôn trọng. Comitee sẽ không chấp nhận một giải pháp trông giống như kế thừa theo cú pháp vì điều này.
v.oddou

4

Còn cái này thì sao? Được, một thể hiện được tạo cho mọi giá trị có thể, nhưng bên cạnh đó nó rất linh hoạt. Có bất kỳ nhược điểm nào không?

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

.cpp:

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;

Sử dụng:

BaseEnum e = DerivedEnum::THREE;

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

Những bất lợi duy nhất mà tôi có thể thấy là tiêu thụ bộ nhớ cao hơn và nó cần nhiều dòng mã hơn. Nhưng tôi sẽ thử giải pháp của bạn.
Knitschi

Tôi cũng đã BaseEnum::icông khai và BaseEnum::maxIriêng tư.
Knitschi

Hàm tạo mặc định được bảo vệ có thể là một vấn đề khi enum cần được sử dụng trong các macro của bên thứ ba hoặc các mẫu yêu cầu hàm tạo mặc định.
Knitschi

3

Chà, nếu bạn định nghĩa enumvới cùng một tên trong lớp dẫn xuất và bắt đầu nó từ mục cuối cùng của đối tượng enumtrong lớp cơ sở, bạn sẽ nhận được hầu hết những gì bạn muốn - enum kế thừa. Nhìn vào mã này:

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

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

1
trong khi mã có thể biên dịch, bạn sẽ không thể sử dụng nó, vì trình biên dịch sẽ không thể chuyển đổi từ base :: ErrorType thành Inherited :: ErrorType.
bavaza

1
@bavaza, chắc chắn, bạn nên sử dụng số nguyên thay vì enum khi chuyển các giá trị của chúng làm tham số.
Haspemulator

2

Như đã nêu bởi bayda, enum's không (và / hoặc không nên) có chức năng, vì vậy tôi đã thực hiện cách tiếp cận sau đây đối với vấn đề khó khăn của bạn bằng cách điều chỉnh Mykola Golubyevphản ứng của:

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

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

2
Có một số vấn đề với giải pháp này. Đầu tiên, bạn đang gây ô nhiễm BaseEnum với LAST không thực sự tồn tại ngoài việc đặt điểm khởi đầu cho DerivedEnum. Thứ hai, điều gì sẽ xảy ra nếu tôi muốn đặt một số giá trị trong BaseEnum một cách rõ ràng mà sẽ xung đột với các giá trị DerivedEnum? Dù sao, đó có lẽ là điều tốt nhất mà chúng tôi có thể làm cho đến nay như trong C ++ 14.
Огњен Шобајић

Giống như tôi đã nói, nó được chuyển thể từ ví dụ trước, vì vậy nó được bày tỏ cách thức mà nó là cho đầy đủ, nó không phải là mối quan tâm của tôi rằng người đăng trước đó có vấn đề về logic trong ví dụ của họ
cảnh giác

2

Bạn có thể sử dụng một dự án SuperEnum để tạo bảng liệt kê có thể mở rộng.

/*** 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

1
Mặc dù là một bài viết cũ, tôi cảm thấy điều này xứng đáng được trả lời. Tôi sẽ đề nghị loại bỏ hàm tạo mặc định và chuyển hàm tạo rõ ràng sang riêng tư. Bạn vẫn có thể bắt đầu biến theo cách bạn đang làm. Tất nhiên bạn nên thoát khỏi const int&cho một đơn giảnint
Moia

2

Khá là hacky nhưng đây là điều tôi nghĩ ra nếu đối phó với enums có phạm vi:

enum class OriginalType {
   FOO,  // 0
   BAR   // 1
   END   // 2
};

enum class ExtendOriginalType : std::underlying_type_t<OriginalType> {
   EXTENDED_FOO = static_cast<std::underlying_type_t<OriginalType>>
                                           (OriginalType::END), // 2
   EXTENDED_BAR  // 3
};

và sau đó sử dụng như:

OriginalType myOriginalType = (OriginalType)ExtendOriginalType::EXTENDED_BAR;

2

Câu trả lời này là một biến thể của câu trả lời Brian R. Bondy. Vì đã được yêu cầu trong một bình luận, tôi sẽ thêm nó làm câu trả lời. Tôi không chỉ về việc nó có thực sự đáng giá hay không.

#include <iostream>

class Colors
{
public:
    static Colors RED;
    static Colors GREEN;

    operator int(){ return value; }
    operator int() const{ return value; }

protected:
    Colors(int v) : value{v}{} 

private:
    int value;
};

Colors Colors::RED{1};
Colors Colors::GREEN{2};

class RGB : public Colors
{
public:
    static RGB BLUE;

private:
    RGB(int v) : Colors(v){}
};

RGB RGB::BLUE{10};

int main ()
{
  std::cout << Colors::RED << " " << RGB::RED << std::endl;
}

Sống tại Coliru


0

Không thể nào.
Nhưng bạn có thể xác định ẩn danh enum trong một lớp, sau đó thêm các hằng số enum bổ sung trong các lớp dẫn xuất.


-2
enum xx {
   ONE = 1,
   TWO,
   xx_Done
};

enum yy {
   THREE = xx_Done,
   FOUR,
};

typedef int myenum;

static map<myenum,string>& mymap() {
   static map<myenum,string> statmap;
   statmap[ONE] = "One";
   statmap[TWO] = "Two";
   statmap[THREE] = "Three";
   statmap[FOUR] = "Four";
   return statmap;
}

Sử dụng:

std::string s1 = mamap()[ONE];
std::string s4 = mymap()[FOUR];
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.