我有一个类向量
class vector3f
{
public:
float x;
float y;
float z;
};
如果我想访问其中一个成员,我必须输入vec.x,vec.y和vec.z. 现在,我有一个应该按索引访问数据成员的算法,例如:
for(int i = 0; i < 3; i++)
{
vec[i] = i*i;
}
在没有索引的情况下执行此操作将导致多个if条件:
for(int i = 0; i < 3; i++)
{
if(i == 0){vec.x = i*i;}
else if(i == 1){vec.y = i*i;}
else if(i == 2){vec.z = i*i;}
}
有没有办法索引数据成员,以便在编译时解析它们? 我不想在向量类中使用数组,因为使用vec.x,vec.y和vec.z访问数据成员对程序员来说更方便。什么是避免这些if条件的最佳解决方案?
答案 0 :(得分:5)
您可以为您的班级重载operator[]
,将这些if
条件(或最好是switch
)本地化为一个地方:
class vector3f
{
public:
float x;
float y;
float z;
float& operator[] (size_t i)
{
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
default: throw "something";
}
}
float operator[] (size_t i) const
{ return (*const_cast<vector3f*>(this))[i];
}
这是实现这一目标的通用和安全的方式。
如果你的类实际上是标准布局类 1 ,正如你所示, if 你知道对齐和填充方案您的编译器/平台,如果你知道这些float
只会在内存中相继排列,你也可以像这样实现运算符:
float& operator[] (size_t i)
{
return (&x)[i];
}
这会将x
视为float
数组的开头。它会更快,但依赖于我上面提到的 ifs ,并且不会进行边界检查。
1 这基本上意味着它没有非空基类,没有virtual
函数,也没有数据成员非标准布局类型。
答案 1 :(得分:1)
在编译时并非绝对,但您可以进行优化。
您可以使用指针到成员变量,然后使用其数组。从:
开始float vector3f::*px;
px = &vector3f::x;
(vec.*px) = 10.2;
然后声明指向成员的数组:
float vector3f::*pArray[3];
分配它们:
pArray[0] = &vector3f::x;
pArray[1] = &vector3f::y;
pArray[2] = &vector3f::z;
使用它们:
(vec.*pArray[0]) = 10.2; // This is vector3f::x
(vec.*pArray[1]) = 1042.5; // This is vector3f::y
并适当地更改循环体以索引适当的指向成员的指针!
答案 2 :(得分:0)
使用references可以获得一个有趣的替代方案。
class vector3f
{
public:
float &x, &y, &z;
float value[3];
vector3f() :
x(value[0]), y(value[1]), z(value[2]){}
};
这里我们将x绑定到值[0],y绑定到值[1],z绑定到值[2] - 它们是别名,用于数组中的条目,但请注意它们确实分配了在类中每个引用额外4个字节。这样,我们可以命名我们的数组以方便用户访问。
我们也可以避免对重载operator [
的运行时调用(但是好的编译器应该在交换机/案例中内联访问)。
使用示例:
vector3f test;
test.value[0] = 227.f;
test.value[1] = 42.f;
test.value[2] = -1.f;
printf("Values are %lf, %lf and %lf.",test.x,test.y,test.z);
结果输出:
Values are 227.000000, 42.000000 and -1.000000.