联合体union在立体几何中的妙用
为了更好地理解三维游戏编程,我开始研究了立体几何,注意,是立体解析几何,里面涉及到了很多元组、向量和矩阵的知识。虽然还有一些不懂,可是这唤醒了我在高等数学中学到的知识,我想以后还是有很大的用处的。
当然,数学是工具,是为我们编程服务的。但是出于对性能和简洁性的敏感,在构建数学库的过程中我千方百计减少我们的代码量。在我所学的封装、继承和多态以及模板中进行选择,其中一个鲜有接触的关键字union映入了我的眼帘。
union,即联合体,主要是为了解决相同的内容用不同的符号访问的问题。在union的内部,多个成员共享相同的内存,有利于实现该联合体用途的多样性。此外联合体可以用来判断大端和小端的问题:
#include <iostream>
union Test
{
int i;
char a;
};
int main( int, char** )
{
using namespace std;
cout << "本程序用来判断是否是大端(Big Endian)还是小端(Small Endian)。\n" << '\n';
Test aTest;
aTest.i = 1;
if ( aTest.a == 1 )
cout << "此计算机是小端。\n";
else cout << "此计算机是大端。\n";
return 0;
}
在这个例子中,如果是小端,那么1放在低字节,这样chara和inti都可以访问到,否则chara和inti访问的是不同的内容。则为大端。
在构建我的3D数学库的时候,我曾经考虑,使用一个通用的类模板Tuple表示元组,通过模板参数2、3和4将其实例化为不同维数的元组。可是除了三维坐标,其它的比如说Size、和Color都可以用元组来表示,那么有什么办法可以让我们少写代码呢?在看了union的用法后,我想到何不使用union来完成这个任务?
// 以下代码需要高级版本C++编译器支持
template <typename T, int n>
union Tuple
{
T m[n];
};
// 特化
template <typename T>
union Tuple<T, 2>
{
Tuple( void ){ }
Tuple( T _1, T _2 )
{
Set( _1, _2 );
}
void Set( T _1, T _2 )
{
m[0] = _1, m[1] = _2;
}
operator T*( void ) // 重载类型转换函数
{
return m;
}
Tuple operator+( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] + obj.m[0];
ret.m[1] = m[1] + obj.m[1];
return ret;
}
Tuple operator-( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] - obj.m[0];
ret.m[1] = m[1] - obj.m[1];
return ret;
}
Tuple& operator+=( const Tuple& obj )
{
m[0] += obj.m[0];
m[1] += obj.m[1];
return *this;
}
//-------------------------------------
T m[2];
struct
{
T x, y;
};
struct
{
T w, h;
};
};
template <typename T>
union Tuple<T, 3>
{
Tuple( void ){ }
Tuple( T _1, T _2, T _3 )
{
Set( _1, _2, _3 );
}
void Set( T _1, T _2, T _3 )
{
m[0] = _1, m[1] = _2, m[2] = _3;
}
operator T*( void ) // 重载类型转换函数
{
return m;
}
Tuple operator+( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] + obj.m[0];
ret.m[1] = m[1] + obj.m[1];
ret.m[2] = m[2] + obj.m[2];
return ret;
}
Tuple operator-( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] - obj.m[0];
ret.m[1] = m[1] - obj.m[1];
ret.m[2] = m[2] - obj.m[2];
return ret;
}
Tuple operator*( const T& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] * obj;
ret.m[1] = m[1] * obj;
ret.m[2] = m[2] * obj;
return ret;
}
Tuple operator-( void )
{
return Tuple( -m[0], -m[1], -m[2] );
}
Tuple& operator+=( const Tuple& obj )
{
m[0] += obj.m[0];
m[1] += obj.m[1];
m[2] += obj.m[2];
return *this;
}
//-------------------------------------
T m[3];
struct
{
T x, y, z;
};
struct
{
T r, g, b;
};
};
template <typename T>
union Tuple<T, 4>
{
Tuple( void ){ }
Tuple( T _1, T _2, T _3, T _4 )
{
Set( _1, _2, _3, _4 );
}
void Set( T _1, T _2, T _3, T _4 )
{
m[0] = _1, m[1] = _2, m[2] = _3, m[3] = _4;
}
operator T*( void ) // 重载类型转换函数
{
return m;
}
Tuple operator+( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] + obj.m[0];
ret.m[1] = m[1] + obj.m[1];
ret.m[2] = m[2] + obj.m[2];
ret.m[3] = m[3] + obj.m[3];
return ret;
}
Tuple operator-( const Tuple& obj ) const// 重载减号
{
Tuple ret;
ret.m[0] = m[0] - obj.m[0];
ret.m[1] = m[1] - obj.m[1];
ret.m[2] = m[2] - obj.m[2];
ret.m[3] = m[3] - obj.m[3];
return ret;
}
Tuple& operator+=( const Tuple& obj )
{
m[0] += obj.m[0];
m[1] += obj.m[1];
m[2] += obj.m[2];
m[3] += obj.m[3];
return *this;
}
//-------------------------------------
T m[4];
struct
{
T x, y, z, w;
};
struct
{
T r, g, b, a;
};
};
// 定义数据类型
typedef Tuple<float, 2> Vector2F, Point2F, Size2F;
typedef Tuple<float, 3> Vector3F, Color3F, Point3F;
typedef Tuple<float, 4> Vector4F, Color4F;
在上面的代码中,我尝试了将联合体和模板相结合,使用了模板偏特化的技术。此外还在union中嵌入了无名的结构体。比如Tuple<float,3>,我既可以访问Tuple<float,3>::x、Tuple<float,3>::y、Tuple<float,3>::z,也可以访问Tuple<float,3>::r、Tuple<float,3>::g、Tuple<float,3>::b。所以用typedef定义向量和颜色,成员可以完全不同,但是它们所指向的内容是相同的。
上述代码可以很顺利地通过,如果你使用的是微软的VC6.0编译器,那么无法支持模板偏特化的特性。此外,一些IDE的自动补全功能在推测联合体模板的成员时候会失效。比如我的QtCreator2.6就无法正确地推测成员。