【问题标题】:Nice way of accessing member variables based on a switch基于开关访问成员变量的好方法
【发布时间】:2013-07-07 18:45:37
【问题描述】:

我正在编写包含多个具有以下模式的函数的代码

memberType* var = NULL;
switch(someVariable)
{
  case 0: var = &object.MemberVariable1; break;
  case 1: var = &object.MemberVariable2; break;
  case 2: var = &object.MemberVariable3; break;
}

成员变量的类型相同。在某些交换机中,有几十种情况,它们使函数体与基本上只是数据的东西混在一起。我想创建一个数组、映射或类似的东西,以便我可以根据输入值访问成员。

我正在考虑有类似的东西

sometype array[size] = {member1, member2, member3}

所以函数可以包含

memberType* var = array[index];

而不是开关。

1. 我的第一个想法是在包含单个成员变量和数组的类中创建一个联合,因此我可以访问单个成员或通过数组 + 索引访问它们。这很丑陋,乍一看从代码中并不明显,并且强制成员 vars 以连续的方式声明。它也不允许我为不同的索引访问相同的成员变量。

2. 拥有一个数组,其中包含指向返回单个成员变量的函数的函数指针,这迫使我创建大量的单行 getter 函数。

3. 有一个静态分配的对象,所以我可以做类似的事情

int offsets[size] = {
*(int*)&staticObject.member1 - *(int*)&staticObject,
*(int*)&staticObject.member2 - *(int*)&staticObject, 
*(int*)&staticObject.member3 - *(int*)&staticObject}

并使用它

var = (memberType*)(*(int*)&object + offsets[index]);

太可怕了。

有什么好方法可以摆脱这种不必要的冗长模式吗?

免责声明:我没有测试示例中使用的代码。这只是为了说明。


编辑:我忘了提到一件重要的事情:我不想因为序列化而改变类的大小 - 我还没有理解我正在使用的实现。

【问题讨论】:

  • C 语言中有很多地图实现。为什么不只使用其中之一?
  • 问题是如何存储对成员变量的访问,而不是容器类型或实现
  • 为什么数组中的成员不以开头?也就是说,当您似乎真的想要一个数组时,为什么还要有离散成员?
  • @JoeZ:因为每个变量都有不同的含义,并且在代码的其余部分中使用不同。该数组仅对带有这些开关的功能有意义。这是我正在重构的旧代码,所以我必须小心。
  • 您可以创建一个 const&[N],或使用可以进行边界检查的 GetMemberAt(int)(可能与您的开关相同)。 At 可能更好。

标签: c++ coding-style readability


【解决方案1】:

您可能想查看pointers to members。这将使您能够更干净地实施您的建议 (3)。

假设你的班级看起来像这样。

class X {
public:
  memberType MemberVariable1;
  memberType MemberVariable2;
  memberType MemberVariable3;
};

现在我们将定义一个指向成员变量的指针数组。

typedef memberType X::*MemberVar;
MemberVar ptrs[3] = {
  &X::MemberVariable1,
  &X::MemberVariable2,
  &X::MemberVariable3
};

那么就可以这样使用数组了。

X obj;
memberType var = obj.*(ptrs[index]);

【讨论】:

  • +1。这正是 OP 试图从头开始发明的东西。
  • 太棒了。我第一次听说这个,但这正是我想要的。不过,数组初始化中的分号应该改为逗号。
【解决方案2】:

encapsulateswitch

  1. 在对象的类中,定义GetMemberVariableAt(int) 并将开关移到那里。
  2. 然后,让您的客户端代码请求object.GetMemberVariableAt(someVariable)

如果您这样做,那么使用讨厌的开关来获取正确的成员变量就不是那么重要了,因为该开关将在您的对象实现内部,而不是涂抹在您的代码中。而且,您有这样的想法,您可以在以后以最小的努力改进它,切换到阵列或任何您的想法(或您的性能要求,也许)想要的。

如果你不控制你的对象的类的源代码,你仍然可以代理它或在它前面放一个适配器,然后编码到代理或适配器。


我在这里给出的解决方案旨在通过降低丑陋因素的重要性,让您更容易在您已经在问题中提供的替代方案之间进行选择。

我应该在switch 和一组成员引用解决方案之间补充一点,我更喜欢switch,因为数据重复可能是潜在错误和复杂代码的丰富来源。如果您可以在内部用数组替换成员变量,我会在switch 上解决这个问题,但如果您必须同时维护数组和GetMemberVariableAt 之外的字段,这就是数据我强烈建议不要重复。

【讨论】:

  • 我的目标是能够以不易出错的方式分别声明与输入值关联的返回值。将 switch 提取到单独的函数并不会使 switch 本身更具可读性。
  • 明白。我不知道有一种风格会在我的脑海中胜过其他人。您自己提出了许多替代方案,但担心它们有多难看。我提供了一个解决方案,它使解决方案的丑陋变得不那么重要,让您的代码清晰,同时让您考虑性能和其他细节。关于您提出的替代方案,我要补充一点,我更喜欢不需要数据冗余的解决方案,即将您的值同时放在数组和实例字段中。所以如果你不喜欢这个开关,用数组替换字段。
  • 要继续并在我上一条评论的基础上进行构建,我更喜欢 switch 而不是混合字段+数组解决方案,因为数据冗余会给您带来潜在错误的风险,而交换机在一些冗长的成本。数组解决方案也付出了代价:您的代码可能会变得更加微妙,而可读性是代码的主要关注点。如果这些字段可以自然地被认为是一个数组,以至于它们感觉组合在一起比作为类的孩子更自然,那么就放开数组。否则,我不介意switch。可读性 + 最小的潜在错误对我有用。
猜你喜欢
  • 2013-02-01
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-25
相关资源
最近更新 更多