联合体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就无法正确地推测成员。