热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

结合C++11的新特性来解析C++中的枚举与联合

这篇文章主要介绍了C++编程中的枚举与联合,结合了范围(或强类型)enumclass类型等C++11的新特性来讲解,需要的朋友可以参考下

枚举
枚举是用户定义的类型,其中包含一组称为枚举器的命名的整型常数。
语法

// unscoped enum:
enum [identifier] [: type]

{enum-list}; 

// scoped enum:
enum [class|struct] 
[identifier] [: type] 
{enum-list};
// Forward declaration of enumerations (C++11):
enum A : int; // non-scoped enum must have type specified
enum class B; // scoped enum defaults to int
enum class C : short;

参数
identifier
指定给与枚举的类型名称。
type
枚举器的基础类型;所有枚举器都具有相同的基础类型。可能是任何整型。
enum-list
枚举中以逗号分隔的枚举器列表。范围中的每个枚举器或变量名必须是唯一的。但是,值可以重复。在未区分范围的枚举中,范围是周边范围;在区分范围的枚举中,范围是 enum-list 本身。
class
可使用声明中的此关键字指定枚举区分范围,并且必须提供 identifier。还可使用 struct 关键字来代替 class,因为在此上下文中它们在语义上等效。
备注
枚举提供上下文来描述以命名常数表示的一系列值,这些值也称为枚举器。在原始 C 和 C++ 枚举类型中,非限定枚举器在声明枚举的整个范围中可见。在区分范围的枚举中,枚举器名称必须由枚举类型名称限定。以下示例演示两种枚举之间的基本差异:

namespace CardGame_Scoped
{
  enum class Suit { Diamonds, Hearts, Clubs, Spades };

  void PlayCard(Suit suit)
  {
    if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
    { /*...*/}
  }
}

namespace CardGame_NonScoped
{
  enum Suit { Diamonds, Hearts, Clubs, Spades };

  void PlayCard(Suit suit)
  {
    if (suit == Clubs) // Enumerator is visible without qualification
    { /*...*/
    }
  }
}

将为枚举中的每个名称分配一个整数值,该值与其在枚举中的顺序相对应。默认情况下,为第一个值分配 0,为下一个值分配 1,以此类推,但你可以显式设置枚举器的值,如下所示:

enum Suit { DiamOnds= 1, Hearts, Clubs, Spades };

为枚举器 Diamonds 分配值 1。后续枚举器接收的值会在前一个枚举器的值的基础上加一(如果没有显式赋值)。在前面的示例中,Hearts 将具有值 2,Clubs 将具有值 3,依此类推。
每个枚举器将被视为常数,并且必须在定义 enum 的范围内(对于未区分围的枚举)或在枚举本身中(对于区分范围的枚举)具有唯一名称。为这些名称指定的值不必是唯一的。例如,如果一个未区分范围的枚举 Suit 的声明如下:

enum Suit { DiamOnds= 5, Hearts, Clubs = 4, Spades };

Diamonds、Hearts、Clubs 和 Spades 的值分别是 5、6、4 和 5。请注意,5 使用了多次;尽管这并不符合预期,但是允许的。对于区分范围的枚举来说,这些规则是相同的。
强制转换规则
未区分范围的枚举常数可以隐式转换为 int,但是 int 不可以隐式转换为枚举值。下面的示例显示了如果尝试为 hand 分配一个不是 Suit 的值可能出现的情况:

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

将 int 转换为区分范围或未区分范围的枚举器时,需要强制转换。但是,你可以将区分范围的枚举器提升为整数值,而不进行强制转换。

int account_num = Hearts; //OK if Hearts is in a unscoped enum

按照这种方式使用隐式转换可能导致意外副作用。若要帮助消除与区分范围的枚举相关的编程错误,区分范围的枚举值必须是强类型值。区分范围的枚举器必须由枚举类型名称(标识符)限定,并且无法进行隐式转换,如以下示例所示:

namespace ScopedEnumConversions
{
  enum class Suit { Diamonds, Hearts, Clubs, Spades };

  void AttemptConversions()
  {
    Suit hand; 
    hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
    hand = Suit::Clubs; //Correct.
    int account_num = 135692;
    hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
    hand = static_cast(account_num); // OK, but probably a bug!!!

    account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
    account_num = static_cast(Suit::Hearts); // OK
}

注意,hand = account_num; 行仍会导致对未区分范围的枚举发生的错误,如前面所示。它可以与显式强制转换一起使用。但是,借助区分范围的枚举,不再允许在没有显式强制转换的情况下在下一条语句 account_num = Suit::Hearts; 中尝试转换。

联合

union 是用户定义的类型,其中所有成员都共享同一个内存位置。 这意味着在任何给定时间,联合都不能包含来自其成员列表的多个对象。 这还意味着无论联合具有多少成员,它始终仅使用足以存储最大成员的内存。
具有大量对象和/或内存有限时,联合可用于节省内存。 但是,需要格外小心才能正确使用它们,因为由你负责确保可始终访问写入的最后一个成员。 如果任何成员类型具有不常用构造函数,则必须编写附加代码来显式构造和销毁该成员。 使用联合之前,应考虑是否可以使用基类和派生类来更好地表示尝试解决的问题。

union [name] { member-list };

参数
name
为联合提供的类型名称。
member-list
联合可以包含的成员。 请参阅“备注”。
备注
声明联合
利用 union 关键字开始联合的声明,并用大括号包含成员列表:

// declaring_a_union.cpp
union RecordType  // Declare a simple union type
{
  char  ch;
  int  i;
  long  l;
  float f;
  double d;
  int *int_ptr;
}; 
int main()
{
  RecordType t;
  t.i = 5; // t holds an int
  t.f = 7.25 // t now holds a float 
}

使用联合
在前面的示例中,任何访问联合的代码都需要了解保存数据的成员。 此问题最常见的解决方案是将联合以及其他枚举成员(指示当前存储在联合中的数据的类型)放入一个结构中。 这称为可区分的联合,下面的示例演示了基本模式。

#include "stdafx.h"
#include 

using namespace std;


enum class WeatherDataType
{
  Temperature, Wind
};

struct TempData
{
  int StationId;
  time_t time;
  double current;
  double max;
  double min;
};

struct WindData
{
  int StationId;
  time_t time;
  int speed;
  short direction;
};

struct Input
{
  WeatherDataType type;
  union
  {
    TempData temp;
    WindData wind;
  };
};

// Functions that are specific to data types
void Process_Temp(TempData t) {}
void Process_Wind(WindData w) {}

// Container for all the data records
queue inputs;
void Initialize();


int main(int argc, char* argv[])
{
  Initialize();
  while (!inputs.empty())
  {
    Input i = inputs.front();
    switch (i.type)
    {
    case WeatherDataType::Temperature:
      Process_Temp(i.temp);
      break;
    case WeatherDataType::Wind:
      Process_Wind(i.wind);
      break;
    default:
      break;
    }
    inputs.pop();

  }
  return 0;
}

void Initialize()
{
  Input first, second;
  first.type = WeatherDataType::Temperature;
  first.temp = { 101, 1418855664, 91.8, 108.5, 67.2 };
  inputs.push(first);

  second.type = WeatherDataType::Wind;
  second.wind = { 204,1418859354, 14, 27 };
  inputs.push(second);
}


在前面的示例中,请注意 Input 结构中的联合没有名称。 这是匿名联合,可以访问其成员,如同它们是结构的直接成员一样。 有关匿名联合的详细信息,请参阅下面一节。
当然,上面的示例演示的问题也可以通过以下方法解决:使用派生自公共基类的类,并基于容器中每个对象的运行时类型对代码进行分支。 这可以生成更易于维护和理解的代码,但是也可能比使用联合更慢。 此外,通过联合可以存储完全不相关的类型,并动态更改存储的值的类型,而无需更改联合变量本身的类型。 因此可以创建其元素存储不同类型的不同值的 MyUnionType 异类数组。
请注意,可能会很容易误用前面示例中的 Input 结构。 完全由用户负责正确使用鉴别器来访问保存数据的成员。 你可以通过使联合成为专用并提供特殊访问函数(如下一个示例所示)来防止误用。
无限制的联合 (C++11)
在 C++03 及更低版本中,联合可以包含具有类类型的非静态数据成员,只要该类型没有用户提供的构造函数、析构函数或赋值运算符即可。 在 C++11 中,消除了这些限制。 如果在联合中包含这样一个成员,则编译器会自动将不是用户提供的任何特殊成员函数标记为已删除。 如果联合是类或结构中的匿名联合,则类或结构的不是用户提供的任何特殊成员函数都会标记为已删除。 下面的示例演示如何处理联合的某个成员具有需要此特殊处理的成员的情况:

// for MyVariant
#include 
#include 
#include 

// for sample objects and output
#include 
#include 
#include 

using namespace std;

struct A 
{
  A() = default;
  A(int i, const string& str) : num(i), name(str) {}
  
  int num;
  string name;
  //...
};

struct B 
{
  B() = default;
  B(int i, const string& str) : num(i), name(str) {}
  
  int num;
  string name;
  vector vec;
  // ...
};

enum class Kind { None, A, B, Integer };

#pragma warning (push)
#pragma warning(disable:4624)
class MyVariant
{
public:
  MyVariant()
    : kind_(Kind::None)
  {
  }

  MyVariant(Kind kind)
    : kind_(kind)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A();
      break;
    case Kind::B:
      new (&b_) B();
      break;
    case Kind::Integer:
      i_ = 0;
      break;
    default:
      _ASSERT(false);
      break;
    }
  }

  ~MyVariant()
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      a_.~A();
      break;
    case Kind::B:
      b_.~B();
      break;
    case Kind::Integer:
      break;
    default:
      _ASSERT(false);
      break;
    }
    kind_ = Kind::None;
  }

  MyVariant(const MyVariant& other)
    : kind_(other.kind_)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A(other.a_);
      break;
    case Kind::B:
      new (&b_) B(other.b_);
      break;
    case Kind::Integer:
      i_ = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
  }

  MyVariant(MyVariant&& other)
    : kind_(other.kind_)
  {
    switch (kind_)
    {
    case Kind::None:
      break;
    case Kind::A:
      new (&a_) A(move(other.a_));
      break;
    case Kind::B:
      new (&b_) B(move(other.b_));
      break;
    case Kind::Integer:
      i_ = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
    other.kind_ = Kind::None;
  }

  MyVariant& operator=(const MyVariant& other)
  {
    if (&other != this)
    {
      switch (other.kind_)
      {
      case Kind::None:
        this->~MyVariant();
        break;
      case Kind::A:
        *this = other.a_;
        break;
      case Kind::B:
        *this = other.b_;
        break;
      case Kind::Integer:
        *this = other.i_;
        break;
      default:
        _ASSERT(false);
        break;
      }
    }
    return *this;
  }

  MyVariant& operator=(MyVariant&& other)
  {
    _ASSERT(this != &other);
    switch (other.kind_)
    {
    case Kind::None:
      this->~MyVariant();
      break;
    case Kind::A:
      *this = move(other.a_);
      break;
    case Kind::B:
      *this = move(other.b_);
      break;
    case Kind::Integer:
      *this = other.i_;
      break;
    default:
      _ASSERT(false);
      break;
    }
    other.kind_ = Kind::None;
    return *this;
  }

  MyVariant(const A& a)
    : kind_(Kind::A), a_(a)
  {
  }

  MyVariant(A&& a)
    : kind_(Kind::A), a_(move(a))
  {
  }

  MyVariant& operator=(const A& a)
  {
    if (kind_ != Kind::A)
    {
      this->~MyVariant();
      new (this) MyVariant(a);
    }
    else
    {
      a_ = a;
    }
    return *this;
  }

  MyVariant& operator=(A&& a)
  {
    if (kind_ != Kind::A)
    {
      this->~MyVariant();
      new (this) MyVariant(move(a));
    }
    else
    {
      a_ = move(a);
    }
    return *this;
  }

  MyVariant(const B& b)
    : kind_(Kind::B), b_(b)
  {
  }

  MyVariant(B&& b)
    : kind_(Kind::B), b_(move(b))
  {
  }

  MyVariant& operator=(const B& b)
  {
    if (kind_ != Kind::B)
    {
      this->~MyVariant();
      new (this) MyVariant(b);
    }
    else
    {
      b_ = b;
    }
    return *this;
  }

  MyVariant& operator=(B&& b)
  {
    if (kind_ != Kind::B)
    {
      this->~MyVariant();
      new (this) MyVariant(move(b));
    }
    else
    {
      b_ = move(b);
    }
    return *this;
  }

  MyVariant(int i)
    : kind_(Kind::Integer), i_(i)
  {
  }

  MyVariant& operator=(int i)
  {
    if (kind_ != Kind::Integer)
    {
      this->~MyVariant();
      new (this) MyVariant(i);
    }
    else
    {
      i_ = i;
    }
    return *this;
  }

  Kind GetKind() const
  {
    return kind_;
  }

  A& GetA()
  {
    _ASSERT(kind_ == Kind::A);
    return a_;
  }

  const A& GetA() const
  {
    _ASSERT(kind_ == Kind::A);
    return a_;
  }

  B& GetB()
  {
    _ASSERT(kind_ == Kind::B);
    return b_;
  }

  const B& GetB() const
  {
    _ASSERT(kind_ == Kind::B);
    return b_;
  }

  int& GetInteger()
  {
    _ASSERT(kind_ == Kind::Integer);
    return i_;
  }

  const int& GetInteger() const
  {
    _ASSERT(kind_ == Kind::Integer);
    return i_;
  }

private:
  Kind kind_;
  union
  {
    A a_;
    B b_;
    int i_;
  };
};
#pragma warning (pop)

int main()
{
  A a(1, "Hello from A");
  B b(2, "Hello from B");

  MyVariant mv_1 = a;

  cout <<"mv_1 = a: " <> c;
}
#include 
#include 
using namespace std;


enum class WeatherDataType
{
  Temperature, Wind
};

struct TempData
{
  TempData() : StationId(""), time(0), current(0), maxTemp(0), minTemp(0) {}
  TempData(string id, time_t t, double cur, double max, double min)
    : StationId(id), time(t), current(cur), maxTemp(max), minTemp(0) {}
  string StationId;
  time_t time = 0;
  double current;
  double maxTemp;
  double minTemp;
};

struct WindData
{
  int StationId;
  time_t time;
  int speed;
  short direction;
};

struct Input
{
  Input() {}
  Input(const Input&) {}

  ~Input()
  {
    if (type == WeatherDataType::Temperature)
    {
      temp.StationId.~string();
    }
  }

  WeatherDataType type;
  void SetTemp(const TempData& td)
  {
    type = WeatherDataType::Temperature;

    // must use placement new because of string member!
    new(&temp) TempData(td);
  }

  TempData GetTemp()
  {
    if (type == WeatherDataType::Temperature)
      return temp;
    else
      throw logic_error("Can't return TempData when Input holds a WindData");
  }
  void SetWind(WindData wd)
  {
    // Explicitly delete struct member that has a 
    // non-trivial constructor
    if (type == WeatherDataType::Temperature)
    {
      temp.StationId.~string();
    }
    wind = wd; //placement new not required.
  }
  WindData GetWind()
  {
    if (type == WeatherDataType::Wind)
    {
      return wind;
    }
    else
      throw logic_error("Can't return WindData when Input holds a TempData");
  }

private:

  union
  {
    TempData temp;
    WindData wind;
  };
};

联合不能存储引用。 联合不支持继承,因此联合本身不能用作基类、继承自另一个类或具有虚函数。
初始化联合
可以通过指定包含在括号中的表达式来在相同语句中声明并初始化联合。 计算该表达式并将其分配给联合的第一个字段。

#include 
using namespace std;

union NumericType
{
  short    iValue;
  long    lValue; 
  double   dValue; 
};

int main()
{
  union NumericType Values = { 10 };  // iValue = 10
  cout <

NumericType 联合排列在内存中(概念性的),如下图所示。

2016118153848038.png (313×163)

匿名联合
匿名联合是声明的没有 class-name 或 declarator-list 的联合。
union { member-list }
匿名联合中声明的名称可直接使用,就像非成员变量一样。 因此,匿名联合中声明的名称必须在周边范围中是唯一的。
除了联合成员数据中列出的限制之外,匿名联合还受其他限制:
如果在文件或命名空间范围内声明联合,则还必须将它们声明为“静态的”。
它们可以只具有公共成员;匿名联合中的私有成员和受保护的成员会生成错误。
它们不能具有函数成员。


推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了MyBioSource转甲状腺素蛋白定量检测ELISA试剂盒的应用方法及特点。ELISA法作为一项新技术在免疫诊断中的应用范围不断扩大,不仅适用于多种病原微生物引起的传染病、非传染病的免疫诊断,也可用于大/小分子抗原的定量检测。ELISA法具有灵敏、特异、简单、快速、稳定及易于自动化操作等特点,是一种早期诊断的良好方法,也可用于血清流行病学调查。MyBioSource转甲状腺素蛋白定量检测ELISA试剂盒使用方法包括对血清和血浆的操作要求。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文介绍了多因子选股模型在实际中的构建步骤,包括风险源分析、因子筛选和体系构建,并进行了模拟实证回测。在风险源分析中,从宏观、行业、公司和特殊因素四个角度分析了影响资产价格的因素。具体包括宏观经济运行和宏经济政策对证券市场的影响,以及行业类型、行业生命周期和行业政策对股票价格的影响。 ... [详细]
  • 宁德时代与第四范式达成合作,将利用第四范式的AI技术,打造规模化的人工智能平台,并将AI技术融入电池生产线。通过全流程AI技术和低门槛的AI生产工具,宁德时代实现了对生产线数据的实时分析与决策。第四范式是一家人工智能技术与服务提供商,其先知平台降低了AI在各行业内的应用门槛。宁德时代是国内具备国际竞争力的动力电池制造商之一,专注于新能源汽车动力电池系统、储能系统的研发、生产和销售。 ... [详细]
author-avatar
I技男
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有