《飞行器实时仿真算法研究本科毕业论文.doc》由会员分享,可在线阅读,更多相关《飞行器实时仿真算法研究本科毕业论文.doc(43页珍藏版)》请在三一办公上搜索。
1、本科毕业设计论文题 目飞行器实时仿真算法研究专业名称飞行器设计与工程学生姓名指导教师毕业时间设计论文 毕业 任务书一、题目飞行器实时仿真算法研究二、指导思想和目的要求1. 通过毕业设计,培养学生解决工程实际问题的独立科研能力,熟悉并掌握从事科学研究的一般方法和基本技能。2. 巩固所学专业基础知识和专业知识,加深对飞行力学、飞行仿真计算等专业知识的理解,完成基于C+的飞行仿真算法开发。3学习、掌握GSL、VC+软件的仿真程序设计,完成基于RTX实时环境算法库开发。三、主要技术指标完成GSL代码由C到C+转化.应用VC+完成GSL的Lib和DLL多种应用开发.完成基于RTX实时仿真应用.四、进度和
2、要求查阅相关资料第3周翻译两篇于论文相关的英文资料第4周学习基于C的GSL开源软件使用第34周完成GSL代码由C到C+转化第58周应用VC+完成GSL多种应用开发第910周完成基于RTX实时仿真应用第1115周仿真结果分析、撰写论文第1617周准备毕业设计答辩第18周五、主要参考书及参考资料1William H. Press.NUMERICAL RECIPES,Third Edition, Cambridge University Press, 20082Ardence Company,RTX 8.1 SDK Release Notes,US, 2007.学生 _ 指导教师 _ 系主任 _目
3、录目 录I摘 要IIIABSTRACTIV第1章 前 言11.1实时飞行仿真算法开发目的和意义11.2国内外发展情况11.3开发内容2第2章 需求分析32.1向量32.2矩阵32.3随机数42.4插值4第3章 算法设计与编程实现53.1工程框架的搭建53.2向量63.2.1向量结构体63.2.2向量类的构架73.2.3 GneVector接口83.2.4 ConstVectorView接口93.2.5 AlmostVector接口103.2.6 Vector接口103.2.7 VectorView接口113.2.8 补充说明123.3矩阵133.3.1矩阵结构体143.3.2矩阵类的构架143
4、.3.3接口153.4随机数153.4.1随机数类的构架163.4.2使用示例183.4.3性能分析183.5插值213.5.1插值基类的构架213.5.2线性插值243.5.3拉格朗日插值253.5.4三次样条插值263.5.5插值接口26第4章 实时环境测试294.1测试环境294.2测试算例1294.3测试算例230第5章 结论31致 谢32参考文献33毕业设计小结34附 录35摘 要本文研究的主要内容是飞行器实时仿真算法,包括向量、矩阵、随机数、插值、微分方程的初值问题。数值计算中的其他部分,具体代码中也有涉及,但由于篇幅所限,未在论文中阐述。在第三章中对每一部分都做了详细的论述,并使
5、用C+语言给出具体的实现和接口,尽量使得算法库具有较高的代码可重复使用性,较快的执行速度,较高的效率和简单的接口。论文结尾给出了该算法库在RTX系统下的测试示例。关键词 通用科学计算库;飞行器仿真算法;多维插值ABSTRACTThis paper is mainly on real-time simulate algorithm of flight vehicle, including vector,matrix,random number,interpolation. Other parts of numeric algorithm are also mentioned in the sou
6、rce code.Each of them is discussed in the third chapter in detail, and by using C+ language, the specific implementation and the user interface is given. The purpose of this is to make this algorithm library reusable, high speed, high efficiency, and simple interface.Some test examples are given in
7、the the end of the paper.KEY WORDS GSL,;flight vehicle simulation alogrithm,;multi-dimension interpolation第1章 前 言1.1实时飞行仿真算法开发目的和意义1.通过毕业设计,培养学生解决工程实际问题的独立科研能力,熟悉并掌握从事科学研究的一般方法和基本技能。2.巩固所学专业基础知识和专业知识,加深对飞行力学、飞行仿真计算等专业知识的理解,完成基于C+的飞行仿真算法开发。3.学习、掌握GSL、VC+软件的仿真程序设计,完成基于RTX实时环境算法库开发。1.2国内外发展情况就目前来看,最常用的
8、科学计算仿真工具应该是matlab莫属了,matlab有很多优点,界面友好,易于操作,强大的工具箱,对矩阵支持良好。但是自matlbab7.0之后,matlab有了自己的虚拟机,不再支持将其代码转为C或C+了,而且用matlab写出的程序不能做成产品,不如其他语言通用。matlab最大的缺点就是循环速度奇慢无比,当然这是解释性语言的通病。编译性语言中,常用的科学计算仿真库也有很多,GSL最为通用,与blitz+和MTL并称为当前三大支持科学计算的数值库,。GSL全称GNU Scientific Library,是GNU项目的组成部分,遵循GPL协议。GSL一个非常全面的数值计算库,使用C语言编
9、写,提供了很多API接口供用户或者其他语言调用。包含数值计算的很多方面,如随机数,向量,矩阵,常微分方程初值问题,特殊函数,积分,线性代数,傅里叶变换,最小二乘法,统计,排列,组合,特征值的求解等等。GSL的优点是运行速度快,尤其矩阵和向量是使用第三方库CBLAS,所以矩阵运算的速度和matlab不相上下(matlab矩阵底层也是用CBLAS库实现的)。缺点是使用非常不方便,用户要负责大量的内存分配和释放工作,时刻注意内存是否泄漏,花费大量精力去维护代码,而不是专注于算法本身。1.3开发内容此次毕业设计的主要内容如下1. 完成GSL代码由C到C+转化。2. 设计并实现多维插值算法3. 应用VC
10、+完成GSL的Lib和DLL多种应用开发。4. 完成基于RTX实时仿真应用。第2章 需求分析2.1向量需要设计一个类,能对数学上的向量进行描述和运算。并且向量中的元素在内存中所占的空间的大小是相同的。满足如下要求。1. 用户不负责向量类对象内部的任何内存申请和删除工作。2. 支持通过操作符和()对元素进行随机存取。3. 支持基本的向量数学运算,如+-*/,范数,支持叉乘等等。4. 需提供最大值,最小值,平均值,求和等函数。5. 支持多线程。6. 提供向量视图功能(可对向量的局部作为一个独立向量进行操作)。7. 支持C+输入和输出,即需重载操作符。8. 支持自定义类型。2.2矩阵设计一个类,能对
11、数学上的矩阵进行描述和运算。并且矩阵中的元素在内存中所占的空间的大小是相同的。所提供的方法有矩阵元素的存取,矩阵和文件之间的读写,赋值,复制,元素之间的交换,行列的交换,基本算术运算(加、减,乘、除),矩阵乘法,求逆,求行列式,LU分解,QR分解,求迹,最大值和最小值等等。1. 用户不负责矩阵类对象内部的任何内存申请和删除工作。2. 支持通过操作符和()对元素进行随机存取。3. 支持基本的向量数学运算,如+-*/,范数,支持叉乘等等。4. 需提供最大值,最小值,平均值,求和等函数。5. 支持多线程。6. 提供矩阵视图功能(可对矩阵的局部作为一个独立矩阵进行操作)。7. 支持C+输入和输出,即需
12、重载操作符。8支持自定义类型。9矩阵求逆,行列式,LU分解,高斯分解,SV分解,求迹,求秩10单位矩阵2.3随机数设计一个随机数类CRandom,满足如下要求:1. 支持多种随机数算法。2. 可以返回每种算法支持的区间中的随机值。3. 可以返回区间0,1)中浮点数。4. 可以返回区间0,n-1中的整数,n由用户提供。5. 可以返回每种算法所能产生的最大随机值,最小随机值。6. 可以保存每种随机数算法的当前状态,并且也可以导入已存的状态。7. 可以动态改变随机数算法。8. 用户不负责类对象内部的任何内存释放和删除工作。2.4插值设计一组类,支持N维插值, 使用拉格朗日和三次样条插值公式。具体需求
13、:1使用纯C+语法和函数,必须具有可移植性。2支持多线程。3不同插值方法的插值接口统一。4设计一种基于xml格式的插值表。5用户不负责插值类内部的任何内存分配和释放工作。6接口简单明了,使用方便。7默认插值表的每一维都是有序表(递增或递减)。8支持的插值算法有线性插值,三次样条插值,n阶多项式插值。第3章 算法设计与编程实现3.1工程框架的搭建工程名字为NSL(nwpu scientific library)工程目录结构图如下其中build目录中为工程生成的可执行文件,包括dll,lib,或者exe。example目录中为工程代码使用的示例。include 目录中为工程中使用到的头文件,可以和
14、build目录一起交付给用户使用。src目录中为工程的实现源码。project目录中为工程文件。所有文件都要包含的预编译头文件是NSL.h,预编译宏定义大致如下:#ifdef WIN32# ifdef NSL_DLL# ifdef DLL_EXPORT# define NSL_VAR extern _declspec(dllexport)# define NSL_EXPORT _declspec(dllexport)# else# define NSL_VAR extern _declspec(dllimport)# define NSL_EXPORT _declspec(dllimport)
15、./* Turn range checking on by default, unless the user definesNSL_RANGE_CHECK_OFF, or defines NSL_RANGE_CHECK to 0 explicitly */#ifdef NSL_RANGE_CHECK_OFF# ifndef NSL_RANGE_CHECK# define NSL_RANGE_CHECK 0# else.#define NSL_SUPPORT_MULTITHREAD上述宏定义非常方便,如果要重新编译此库文件,若在windows环境下,则需要同时定义WIN32,NSL_DLL,和D
16、LL_EXPORT即可。使用此库的头文件时,只需同时定义WIN32,NSL_DLL即正确链接。此数学库支持数组边界检查,当然如果对速度要求高的话,在编译的时候可以通过设置NSL_RANGECHECK_OFF关闭内存检查,以编译出对边界不做检查的可执行文件。最后一行宏定义是决定此库是否支持多线程,如果定义了NSL_SUPPORT_MULTITHREAD,程序中很多计算过程将使用局部变量,以便在多线程环境下不会出现多个线程对同一个变量的同时访问。如果没有定义的话,那么那些计算过程将使用全局静态变量,以提高计算速度。3.2向量GSL提供的向量类型使用起来非常麻烦,由于是对C语言中的数组进行包装,所以
17、用户时刻都要注意内存的变化,花费大量精力减少内存泄露,并且每种数据类型都有不同的函数版本,且不支持用户自定义类型,所以我决定使用GSL的算法,重写构架向量类,并且给出C+的接口,支持多种类型,包括未知的用户自定义类型,尽量减少用户对程序本身的维护工作量,使用户能专注于算法本身。3.2.1向量结构体既然已经选择丢弃GSL提供的向量结构体,那么就要重新定义或选择新的向量类型。由于要支持多种类型,所以最好的方法是使用模板实现,现成的有C+标准库中的vector模板类和valarray模板类,vector和valarray各有特点,vector模板类支持泛型运算,内存可动态改变,支持元素的插入和删除,
18、但是对元素的数学运算支持不好,没有重载一些基本的运算符,更没有重载sin,cos等数学函数。valarray模板类不支持泛型运算(接口上),内存不可动态改变,元素的插入和删除也无从谈起了,但是它对元素的数学运算支持非常好,对+,+=,*=,sin,cos等等操作符都进行了重载,使用非常方便。所以这里我使用valarray模板类作为我的内置向量类型,对其进行包装,增加方法,给出C+接口。3.2.2向量类的构架根据需求分析,首先需要一个Vector向量模板类,还需要一个VectorView向量视图模板类,VectorView貌似可以继承(public)于Vector类,这样就拥有了Vector类的
19、所有方法,也就可以对向量的局部进行操作了,可是在代码初期的设计中,发现这样根本行不通。首先,视图类把向量类的resize函数也继承了,但是对一个视图重新分配内存是没有意义的,也是不允许的。后来我试着用private继承方式,然后在视图类中重写需要的函数,重写非常简单,只是对基类函数的调用而已。可是新问题又出现了,由于private继承方式会把所有的函数都继承为私有函数,包括构造函数,派生类对象无法转换为基类对象,当然C+的多态特性也就无法发挥了。其次,当新的需求来临时,我的代码工作的很不正常,比如我需要视图类有两种形式,一种是可变视图,一种是常视图。可变视图是对其所指向的向量拥有读写权限,而常
20、视图则只有读权限。考虑如下情况:Vectorvec(10); /实例化一个可变向量对象,类型为double,有10个元素。const Vectorc_vec(10); /实例化一个常向量对象,类型为double,有10个元素。VectorView view(vec);/构造一个视图对象,其指向的对象可变,指向vec的所有元素。VectorView c_view(c_vec);/构造一个视图对象,其指向的对象不可变,指向vec的所有元素。此时VectorView类内部的实现将非常复杂,会拥有两个非常相似的构造函数,而且编译器会给出警告。由于视图类内部是不存储元素的,只是当做一个窗口,所以视图类内
21、部肯定会有一个指向向量结构体的指针,那么这个指针该是什么属性?const or non-const ,鬼知道。这时候通过一个视图类和大量的const属性来使代码正常工作是件非常困难的事,无法预料什么时候会发生错误,而且编码时代码混乱不堪,这不是我想要的。所以仅有两个类是不够的,事实上需要5个类来实现需求分析中的功能。GneVector一般矩阵类,AlmostVector类,ConstVectorView常视图类,VectorView类和Vector向量类,它们之间的继承关系如下:图3-1 继承关系图根据类的抽象设计原则,最底层的类应该具有最通用的方法和数据,若需要使用未知的方法,可将此方法声明
22、为虚函数,留给派生类取定义。从上面的构架图中可知,GneVector类为最底层基类, 应该具有最通用的方法和数据。由于视图类也是继承于GneVector,所以GneVector类不应该包涵数据,但是要用到有些方法要用到向量元素,所以可以通过虚函数来取得派生类的数据。3.2.3 GneVector接口根据以上的分析就可以写出GneVector类的接口/基类template class GneVector public:/constructorGneVector();virtual GneVector();/三个类都要重写的方法virtual const slice& getSlice() con
23、st = 0;virtual const valarray& constVal() const = 0;/三个类公有的方法size_t size() const return getSlice().size() ;size_t start()const return getSlice().start() ;size_t stride() const return getSlice().stride() ;size_t index(size_t i) const return getSlice().start() + i*getSlice().stride() ;.;以上代码省略了很多函数和其具
24、体实现,注意到所有的成员函数都是const属性函数,这是因为这些方法都是对Vector、VectorView、和ConstVectorView类进行泛化得出的共有方法,至于其他的方法应该根据特点归入某个类,然后具体实现。3.2.4 ConstVectorView接口由于ConstVectorView继承于基类,有些方法已经在基类中实现,所以该类本身比较简单,提供构造函数,视图函数,和元素存取功能即可。template class ConstVectorView : public GneVector /常视图private:const valarray* pVectorData_;slice v
25、iewSlice_;public:/重写基类的虚函数virtual const slice& getSlice() const;virtual const valarray& constVal() const ;/以下两个构造函数是为了 /Vector - ConstVectorView/VectorView - ConstVectorView/ConstVectorView - ConstVectorViewConstVectorView(const GneVector & vec);ConstVectorView(const GneVector & vec, const slice& s)
26、;.T operator (size_t i) const return cref(i) ;T operator ()(size_t i) const return cref(i) ; / end of class ConstVectorView3.2.5 AlmostVector接口AlmostVector类的存在时为了对Vector类和VectorView类解耦,也可以大幅度提高代码的复用率。接口大致如下:/VectorView and Vector should inherit this classtemplate class AlmostVector : public GneVecto
27、rpublic:AlmostVector();virtual AlmostVector();/override this function in VectorView and Vectorvirtual valarray& val() = 0;/VectorView and Vector 的共有函数/subVectorConstVectorView subVector(const slice & s)const;3.2.6 Vector接口由于前面的设计比较合理,现在对Vector类编码时非常简单,很多方法已经通过两个基类实现过了。但是注意Vector类包含向量元素, 并且提供尽可能多的构造函
28、数,以便使用能使不同的数据类型转为Vector类型。接口大致如下:/向量类template class Vector : public AlmostVectorprivate:/保存向量元素valarray vectorData_;sliceslice_;public:/重写基类的虚函数virtual const slice& getSlice() const ;virtual const valarray& constVal() const;/由STL valarray 构造Vector(const valarray& val) ;Vector(const valarray& val, co
29、nst slice& s);.Vector(const T * p, size_t n) ;/以下两个构造函数是为了 /Vector - Vector/VectorView - Vector/ConstVectorView - VectorVector(const GneVector & vec) ;Vector(const GneVector & vec, const slice & s);3.2.7 VectorView接口template class VectorView : public AlmostVector /可变视图private:valarray* pVectorData_;
30、slice viewSlice_;public:/重写基类的虚函数virtual const slice& getSlice() const ;virtual const valarray& constVal() const;/下面两个构造函数是为了/Vector - MatrixView/VectorView - VectorViewVectorView(AlmostVector & vec);.virtual valarray& val() ;.;3.2.8 补充说明上面接口中使用到的slice类型是C+标准库中valarray类的组成部分,大致结构如下:class slicepublic
31、:.protected:size_t _Start, _Len, _Stride;可以看到slice类主要保存三个数据,即1 向量的第一个元素的下标,_Start2 向量的长度,_Len3 步长,即每个元素之间的间隔,_Stride这种类型可以完全使用到我们的视图类中,这也是为什么选择valarray模板作为内置向量类型的原因之一。上面对接口的陈述中没有提到Vector类型的输入和输出,这里作已简要说明。由于输出只需要对对象具有只读权限即可,而输入则要求对对象的读写权限。所以输出可以放到GneVector底层基类中,作为所有派生类的共有函数。而输入则需放到AlmostVector类中,作为Ve
32、ctor和VectorView类的共有函数。决定某个函数放在基类还是派生类需要很周全的考虑,基类和派生类可以比作一个杠杆,不能一头很重,一头很轻,代码失衡会导致后期的编码和维护非常困难。输入输出函数的声明如下:friend std:ostream& operator ( std:ostream &os, const GneVector &avec);friend std:istream& operator ( std:istream &is, AlmostVector &avec);最后介绍本类初始化时的一个特点。初始化一个向量对象有很多种方法,最直接的方法是给每一个元素赋值,如下:for(s
33、ize_t i=0; iN; +i) vi = /* whatever */但是这样很不方便,尤其是当我们想给v赋一系列不同的值时,在for循环中是比较麻烦的。当然也可以用下面这种方式实现:double var5 = 1.2, 3.2, -9.2, -1, 3.3 ;Vector v(var, 5);但这种方法的不便之处是使用了局部变量。依我来看,最理想的方法是把初始化列表放到构造函数中,如下:Vector v( 1.2, 3.2, -9.2, -1, 3.3 , 5); / Error invalid不幸的是这不符合C+的语法规则,变通一下,使用如下方式:Vector v(double) 1
34、.2, 3.2, -9.2, -1, 3.3 , 5); / Usually OK这种方法通常是不会错的,但是也有可能有些编译器会报错,我也不知道这算不算标准C+语法,因为没有一本C+书(我读过的)讲到过这种初始化方式。为了解决这个问题,我使用了操作符,把我的类对象当做标准输出。从而可以使用如下方式进行初始化:Vector v(5);v 1.2, 3.2, -9.2, -1, 3.3; /或者v 1.2 3.2 -9.2 -1 3.3;输入数据的个数必须和对象的大小一致,否则会触发库中的出错处理函数。上面的方式的优点是,初始化列表里的数据不限制为常量。通常来说,初始化列表必须是在编译时可确定的
35、,但用上面的方式,初始化列表里的元素可以使变量,甚至是一个函数的返回值,只要能转换为Vector模板对应的实例化类型就可以了。当然v也可以在其他任何时候重新使用此方法赋值。另外,由于使用标准库的valarray模板,所以在所有这些类的实现中,甚至没有主动使用C+标准内存分配和删除操作符new,delete,一个也没有出现。最后,GneVector类和AlomostVector类都含有纯虚函数,所以无法实例化,这正是我想要的,给用户使用的类是Vector、VectorView和ConstVectorView三个类。3.3矩阵GSL提供的矩阵类型使用起来非常麻烦,由于是对c语言中的数组进行包装,所
36、以用户时刻都要注意内存的变化,花费大量精力减少内存泄露,并且每种数据类型都有不同的函数版本,且不支持用户自定义类型,所以我决定使用GSL的算法,重写构架矩阵类,并且给出C+的接口,支持多种类型,包括未知的用户自定义类型,尽量减少用户对程序本身的维护工作量,使用户能专注于算法本身。3.3.1矩阵结构体根据上一节中向量类的分析可知,valarray是个非常方便好用的标准模板,所以这里也使用它作为我们的向量结构体。但是如何把它当做二维数据来读取是个问题。这里我写了个很简单的类mslice,类似于上节中的slice,但是适用于二维数据。class mslice public:.private:size
37、_t startRow_;size_t startCol_;size_t rows_;size_t cols_;mslice类保存4条信息:1 startRow_,起始行。2 startCol_,起始列。3 rows_,行数。4 cols_,列数。根据这四条信息就可以确定当前的valarray变量是如何表示一个矩阵的。3.3.2矩阵类的构架由于矩阵和向量的需求分析有很多相似的地方,比如都要求有视图类,所以可以使用向量类的构架方法。构架如下:图3-2 继承关系图3.3.3接口还有5个类接口和上节中的Vector类相似,由于基类设计良好,这些派生类的实现非常简单,在此不一一列出,详见原代码。3.4
38、随机数一个好的随机数生成算法必须能够在理论上和统计上同时满足所需特性。满足理论上的随机是很难的,但我们也可以接受那些具有长周期,低的序列相关性的算法。对随机数算法的测试通常是使用数值仿真来做统计测试,用随机数来估计一些能用概率论求解出精确值的量,然后和精确值做比较即可测量该算法的随机性。GSL提供了大量的随机数生成器,但是由于给出是C的接口,使用起来非常麻烦,用户要负责大量的内存开辟和销毁工作。我要做的就是使用GSL提供的算法,给出随机数生成器的C+统一接口,并且在运行时可以改变随机数算法和种子。每一个随机数的实例都有自己的状态量,即可以同时使用多个相同的随机数算法实例,它们之间是相互独立的,
39、互不影响的,所以可以在多线程环境下运行。需要注意的是,随机数生成器并不是正弦或余弦等函数,对随机数生成器的连续调用会产生不同的值,这正是我们想要的特性。但为了达到这种效果,生成器必须对一些状态量进行跟踪,某些情况下,这些状态是个整数(比如上次生的随机数),但在更多的情况中,这些状态量会很复杂,有可能是一个数列,而且每种随机数生成算法使用的状态量的大小和个数都不一样。但是用户使用的时候,不必关心状态到底是怎么变的,这是算法内部要考虑的事。3.4.1随机数类的构架类名为CRandom,首先分析GSL提供的随机数算法里的共有属性,所有的算法都有种子的设置,最大值,最小值,当前的状态量,算法名称,当然
40、最重要的是有返回介于最大值和最小值之间随机数的方法。至于使用哪种算法可以在构造函数里使用枚举变量进行选择,同时提供一个改变算法的成员函数已实现运行时改变算法。所以类CRandom的接口大致如下class CRandompublic:/*构造函数提供默认随机数算法,也可以动态设置*/CRandom(RNG_ORDER type = RNG_TAUS);virtual CRandom();/*设置种子,任何时候都可以重新设置种子*/void SetSeed(unsigned long int seed);/*返回min,max区间的一个整数, b_pos为true时,返回非零值*/unsigned
41、 long int Rand(bool b_pos = false);/*返回当前使用的算法名称*/const char* Name();/*打印所有的算法及其对应的枚举量*/void printAllNames();/*返回当前算法所能产生的最大随机数 */unsigned long int Max();unsigned long int Min();/*改变所使用的算法 */bool ChangeType(RNG_ORDER type);/*省略代码*/;其中枚举结构体如下enum RNG_ORDER_TRNG_BOROSH13,/borosh13RNG_COVEYOU, / coveyo
42、uRNG_CMRG,/ cmrg/*由于算法很多,在此省略*/;下面考虑成员变量。由于在构造函数中假设每种算法都对应一个枚举量,所以自己构建一个堆栈,把每种算法的指针按照枚举结构体的顺序压栈,在此使用C+标准库中的vector模板构造堆栈,/* gsl_rng_type 为算法结构体,每种算法都维护这样一个结构体*/typedef vector TypeArray;/*type_stack为我们构造的堆栈, 由于是此类对象所共享的变量,所以设为静态*/static const TypeArray type_stack; 静态变量的初始化需要类的静态函数static TypeArray Type
43、Init()CRandom:TypeArray res;res.push_back(gsl_rng_borosh13);res.push_back(gsl_rng_coveyou);res.push_back(gsl_rng_cmrg);/*省略部分代码*/return res;所以type_stack 的初始化可写成如下形式const CRandom:TypeArray CRandom:type_stack = CRandom:TypeInit();成员变量还需要一个能表示当前算法的指针const gsl_rng_type* rng_type;现在一切都非常明了,构造函数的具体实现也是非常简单CRandom:CRandom(RNG_O