510 likes | 652 Views
6.11 宽位字符串映射. 宽位字符串映射与字符串映射基本一样。主要是由函数 wstring_alloc,wstring_dup 和 wstring_free 来完成内存的分配和释放。. 定点数据类型的映射. C++ 中没有定点数据类型,因此 C++ 对定点数据类型的支持与运算是由一个类和一组重载的运算符函数提供。 namespace CORBA{ // …… class Fixed{ // …… };} 通过这一映射,就可以在 C++ 中使用定点数的数值,并对它们进行计算。. 定点数据类型的映射. 6.12.1 构造函数
E N D
6.11 宽位字符串映射 • 宽位字符串映射与字符串映射基本一样。主要是由函数wstring_alloc,wstring_dup和wstring_free来完成内存的分配和释放。
定点数据类型的映射 • C++中没有定点数据类型,因此C++对定点数据类型的支持与运算是由一个类和一组重载的运算符函数提供。 namespace CORBA{ //…… class Fixed{ //……};} 通过这一映射,就可以在C++中使用定点数的数值,并对它们进行计算。
定点数据类型的映射 6.12.1 构造函数 通过构造函数将完成这一转换: Fixed f=999;// fied<3,0> Fixed f1=1000.0;// fied<4,0> Fixed f2=1000.05;// fied<6,2> Fixed f3=0.1;// fied<18,17> Fixed f=1E30;// fied<31,0> Fixed f=1E29+0.89;// fied<31,1>-----------1E29+0.8 在初始化时由于二进制数表示上的特殊性,也可能会产生一些变化,例如在许多现实中0.1的实际值是0.10000000000000001。还可能被截断如1E29+0.89被截断为1E29+0.8。
定点数据类型的映射 • 对超过31个的整数位的数值进行初始化会产生一个DATA_CONVERSION异常。 • 构造字符串类型的Fixed值遵循IDL定点数常量的规则,字符串前面的0和后面的0被忽略掉。“d”或“D”是可选的。例如: Fixed f1=“1.2”; fixed<2,1> Fixed f1=“01.20D”; fixed<2,1> 。
6.12.2 存取函数 fixed_digits和fixed_scale成员函数分别返回数值位数和小数位数的值。例如: Fixed f=“3.14D” cout<<f.fixed_digits()<<endl; //打印3 cout<<f.fixed_scale()<<endl; //打印2 6.12.3 转换运算符 LongLong转换运算符将一个Fixed值转换为LongLong值。 LongDouble转换运算符将一个Fixed值转换为LongDouble值
定点数据类型的映射 • 6.12.4 截断与舍入 truncate成员函数返回一个包括指定的数值位数与小数位数的新的Fixed值,并且在需要时,将小数位数进行截断:例如: Fixed f=“0.99”; cout<<f.truncate(0)<<endl; //打印0 cout<<f.truncate(1)<<endl; //打印0.9 cout<<f.truncate(2)<<endl; //打印0.99 round成员函数返回一个包含指定的数值位数与小数的新的Fixed值,并舍入为指定的小数位数: Fixed r; Fixed f1=“0.4”; Fixed f2=“0.45”; Fixed f3=“-0.445”; r=f1.round(0); //0 r=f1.round(1); //0.4 r=f2.round(0);//0 r=f1.round(1);//0.5 r=f3.round(1);//-0.4 r=f1.round(0);//-0.45
6.13 结构的映射 • 6.13.1 定长度结构的映射 IDL结构使用相对应的成员映射到C++结构。例如: struct Details{ double weight; unsigned long count;}; 映射为: class Details_var; struct Details{ CORBA::double weight; CORBA::unsigned long count; typedef Details_var _var_type; //成员函数……};
结构的映射 • 可以在代码中使用生成的结构,和使用C++中的结构一样。例如: Details d; d.weight=9.5; d.count=12; C++允许静态初始化聚集。 聚集:一个一个类、结构或数组中没有用户说明的构造函数、基类、虚拟函数或私有函数和保护的非静态数据成员,那么这个类、结构或数组都是一个聚集。上述的结构就是一个聚集,因此可以如下初始化: Details d={9.5,12};
6.13.2 变长度结构的映射: 对于变长度的结构,C++映射就必须解决内存管理的问题。例如: struct F{ double num; string al;}; • 映射如下: class F_var; struct F{ CORBA::Double num; CORBA::String_mgr al; typedef F_var var_type; //成员函数…… };
结构的映射 • 在此IDL字符串被映射为一个String_mgr类型,而不是String_var或char *类型。String_mgr类似于String_var,其区别就是String_mgr类中缺省的构造函数将字符串初始化为空字符串,而不是初始化为空指针。 将篏套的类型初始化为空字符串很有用,因为这就意味着,不需要在将用户自定义类型传递给一个IDL接口之前,对它内部的所有字符串成员显示进行初始化。(因为将一个空指针传递给一个IDL接口是非法的。)。不要在应用程序中将String_mgr作为一个类型来使用。这样的话,代码就不可移植。要用String_var。 除了将字符串初始化为空字符串之外,String_mgr与String_var一样,用于内存的自动管理,可以相互赋值。
结构的映射 如果一个结构包含一个变长数据类型,结构会管理这一变长类型的内存。因此只需要考虑最外部的内存的内存管理,例如: { F c; c.num=1.0/3.0; c.al=CORBA::string_dup(“one”); } //没有内存泄漏 以上定义了一个局部变量c,同时结构中的构造函数对结构中的成员进行初始化。对num成员,构造函数没有做什么。然而,al成员是一个篏套字符串,因此构造函数将它初始化为一个空字符串。 为了给al赋值,必须要分配内存,al再次负责这一内存的释放(赋值会对al调用operator=(char *))。
结构的映射 • 当c离开作用域后,它的缺省析构函数会释放所有成员的内存,并且调用al的析构函数,而al的析构函数则会调用CORBA::string_free。这就意味着,当c离开作用域时不会由内存的泄漏。 不能对c进行静态初始化。 6.13.3 结构的内存管理 可以将结构与程序中的其它变量进行同样的处理。程序会自己完成大部分的内存管理工作,这样就可以自由地将结构和结构成员赋给其它地结构和结构成员。
结构的映射 { struct F c1;struct F c2; struct F c3; c1.num=.5;c1.al=CORBA::string_dup(“one”); c2.num=.25;c1.al=CORBA::string_dup(“two”); c3.num=.125;c1.al=CORBA::string_dup(“three”); c2=c1; //深层次赋值 C3.al=c1.al; //深层次赋值 C3.num=1.0 C3.al[3]=‘\0’; //不会影响c1和c2 C1.al[0]=‘O’; //不会影响c2和c3 C1.al[4]=‘H’; //不会影响c2和c3 }//全部释放
在结构中,结构及其成员赋值进行的是多层次地拷贝。而且,当结构删除后,由三个字符串所占据地内存由相应地String_mgr析构函数进行自动释放。在结构中,结构及其成员赋值进行的是多层次地拷贝。而且,当结构删除后,由三个字符串所占据地内存由相应地String_mgr析构函数进行自动释放。 如果要处理动态分配的结构的话,可以使用new和delete: F * fp=new F; fp->num=335.0/113; fp->al=CORBA::string_dup(“four”); delete fp; 这里不需要调用用于内存分配与释放的辅助函数。
结构的映射 • 6.13.4 包含结构成员的结构 对于结构成员本身就是结构的情况,不需要使用特殊的映射规则,只不过在调用时需要再加上取成员运算符(.)。例如: c1.result.num; 其中result是一个结构变量。
6.14 序列的映射 • 6.14.1 无界序列的映射 IDL序列可以映射成类似于向量的C++类,而向量中的元素数目是可变的。每一个IDL序列都可以映射成一个单独的C++类。例如: typedef sequence<string> StrSeq; 映射如下: class StrSeq_var; class StrSeq { public: …… };
对其解释如下: 1. StrSeq() 其函数是缺省构造函数。这个函数将创建一个空序列。调用一个缺省构造的序列的length存取函数将返回数值0。 • 2. StrSeq(const StrSeq &) StrSeq & optertor=(const StrSeq &) 此为拷贝构造函数和赋值运算符。它们都进行多层次的拷贝。赋值运算符再创建源序列的拷贝之前先撤消目标序列。如果序列中的元素是变长的,那么这些元素就用它们的拷贝构造函数进行多层次的拷贝。目标序列的内部最大值设为源序列的内部最大值。 3. ~StrSeq() 析构函数用来撤消一个序列。如果序列中包含变长的元素,那么这些元素的动态内存也将被释放。
序列的映射 4. CORBA::Ulong length() const length存取函数只是返回序列中当前的元素的数目。 5. Void length(CORBA::Ulong newlen) 此函数为长度修改函数,可以修改序列的长度。 增加序列长度会创建newlen-length个新的元素。增加到尾部。由缺省的构造函数来完成对增加元素的初始化。(如果添加的元素是字符串或包含字符串的复杂类型,那么字符串被初始化为空字符串。)
序列的映射 序列长度的减小可以在序列尾部撤消length()-newlen个元素而截断序列。如果通过减小长度的方法截断一个序列,那么被截掉的元素会永久地被撤消掉。 6. CORBA::String_mgr & operator[](CORBA::Ulong idx) const char* operator[](CORBA::Ulong idx) const 下标运算符提供了对元素地访问功能。 序列地下标由0到length()-1。如果下标超出序列地长度地话,会产生不可预料地结果。
序列的映射 • 序列地简单使用 一旦序列离开作用域,它就会调用其元素析构函数释放所有元素。 为了管理由堆分配的序列,可以使用new和delete; StrSeq * sq=new StrSeq; sq->length(4); for(……) (*sq)[i]=values[i]; //…… delete sq; 在此使用的是(*sq)[i]=values[i],这将间接引用这个指针,间接引用这个指针是必要的,因为需要用于下标运算符的StrSeq类型的表达式。如果用sq[i]=values[i]就会产生错误,编译器会假定在处理序列的数组,并且将const char *赋值给序列的第个i元素,这样会造成编译时的错误。
序列的映射 • 控制序列的最大值 当构造一个序列变量时,可以通过最大值构造函数提供一个序列中元素的最大数目: StrSeq myseq(10); //打算输入10个元素到序列中 myseq.length(20); //最大值没有限制序列的长度 for(CORBA::ULOng i=0; i<myseq.length();i++) //初始化 即使代码中使用的最大元素数目是10个,序列中也会添加20个元素。这样做完全没问题,序列中会添加一些附加的元素以增加其长度。
提供序列的最大值主要与内部管理缓冲区有关。如果使用最大值构造函数,序列会把一个内部的最大值设为至少与所给的指一样大的值。此外,序列保证了在当前长度没有超过最大值时,不会再次为元素分配内存。提供序列的最大值主要与内部管理缓冲区有关。如果使用最大值构造函数,序列会把一个内部的最大值设为至少与所给的指一样大的值。此外,序列保证了在当前长度没有超过最大值时,不会再次为元素分配内存。 还有可以提供高效。如果序列能够知道我们所想要的元素数目,那么它就可以更加有效的分配内存。减少了内存分配函数的 调用次数,并且减少了在增加序列长度时拷贝元素的次数。
序列的映射 • 注意: (1) 映射并不是每次调用最大值构造函数时,它会预先分配内存。相反,直到第一个元素创建后才会分配内存。 (2) 映射并不是保证最大值构造函数分配的正好是序列所需的内存,它可能分配的更多一些。 (3) 映射并不保证最大值构造函数一次就能序列元素所需的内存,它可能会在几个不连续的缓冲区中分配序列元素的内存。
(4) 映射并不保证序列元素占据一块连续的内存区域。为了避免再次分配元素,序列在扩展时可能会添加一些新的不连续的缓冲空间。 (5) 映射并不保证增加序列的长度会立即缺省构造新创建的元素,映射的实现会直到赋给一个新的元素后才进行元素的构造,在这个时候才会用拷贝构造函数创建这个元素。 如果知道元素的最大数目,就可以使用最大值构造函数来获得更好的运行时性能。负责不要用这个函数。 不要使用指向序列元素的指针。如果要只用的话,那么就必须非常注意再次分配内存问题。因为再次定位将会使指针失效。
序列的映射 • 6.14.2 有界序列的映射 除了序列的最大值由所生成的类提供之外,有界序列的映射与无界序列的映射一样。 有界序列与无界序列的区别只是在于,有界序列中没有最大值构造函数,数据构造函数不会接收一个最大值参数。 有界序列的长度超出最大值的话,那么就会出现不可预料的结构,往往使核心存储。 • 6.14.3 序列使用中的一些限制 1. 元素的插入与删除 序列映射中只是在序列的尾部改变序列的长度。如果想要将一个元素插入到序列中,那么就必须把元素拷贝到插入点的右侧方来把序列分开。下面的附注函数预先把元素插入到序列中一个指定的位置。如果指定位置的下标与序列的长度相同,则会插入在序列的尾部添加这一元素。函数中只能使用0到length()范围内的下标值。
序列的映射 void pre_insert(……) { seq.length(seq.length()+1); for(CORBA::Ulong I=seq.length()-1;I>idx;I--) seq[I]=seq[I-1]; seq[idx]=elmt;//idx为此元素要插入的位置,elmt的值 } 这段代码在原序列中增加一个元素,然后从序列的插入点到尾部一个一个拷贝元素,从而将序列分开,最后给新的元素赋值。 删除元素可以用同样的代码,需要在删除点的左侧把序列连接起来: void remove(……) { for(CORBA::Ulong I=idx;I<seq.length()-1;I++) seq[I]=seq[I+1]; seq.length(seq.length()-1); }
序列的映射 • 如果经常进行插入与删除,尤其是对于具有复杂类型元素的长序列,这一性能就变得不太理想。在这种情况下,最好选择更为适合的数据类型。 • 6.14.4 序列的使用规则 下面是安全使用序列的一些规则: (1) 不要去考虑什么时候会调用构造函数和析构函数。序列映射的实现完全可能因为效率上的原因而延迟元素的构造或释放。只能假定元素在第一次赋值时由拷贝生成,在第一次访问时进行缺省的构造,在序列的长度减小时或离开作用域后撤消。 (2) 如果release标志为false,就不要将序列传递给一个函数,以对序列进行修改。如果序列没有对缓冲去的所用权的话,被调用的函数在改变序列元素时可能造成内存泄漏。 (3) 避免使用复杂类型的数据构造函数,对于复杂类型,数据构造函数并没有任何优势,只会使源代码更为复杂。 (4) 记住,序列长度超出当前最大值的话会使元素在内存中再次定位。
序列的映射 (5) 不要将序列的下标值超出序列的当前长度 (6) 不要使有界序列的长度增长到超出它的边界 (7) 不要使用数据构造函数或缓冲区操作函数,除非确实需要。对缓冲区进行直接操作容易造成潜在的内存管理的错误。(因为数据构造函数的问题很多,尽量不要使用此函数,因此没有介绍。)
6.15 数组的映射 • IDL数组可以映射成具有对应元素类型的C++数组。字符串元素被映射成String_mgr。这里的关键之处是字符串元素初始化为空字符串,否则的话,字符串元素类似于String_var(也就是说,需要考虑内存管理)。 • 内存分配函数返回一个空指针,以表示函数调用失败,并不发送CORBA或C++异常。
内存分配函数使用代码中生成的数组的切片类型。一个数组的切片是第一维元素的类型(或者,对于二维数组就是行的类型)。在C++中,数组的表达式可以转换成指向第一个元素的指针,而切片类型使该类型的指针的说明变得简单。对于一个数组类型T,指向第一个元素的指针可以说明为T_slice *。因为IDL数组映射为C++的实际数组,所以可以通过指针算法来遍历数组的元素。 • StrArray_copy函数用于对数组的内容进行深层次拷贝,使用时既不需要对源数组进行动态分配,也不需要对目标数组进行动态分配。这一函数可以有效地实现数组地赋值。(因为IDL数组被映射为C++数组,而C++数组并不支持数组地赋值,所以映射不能提供用于数组赋值地重载运算符。)
6.16 联合的映射 • IDL联合不能被映射为C++联合,变长度地联合成员被映射成类,但是C++不允许联合中包含带有特殊构造函数地类成员。此外,C++联合不带有判断功能。为了解决这些问题,IDL联合被映射成C++类。例如: union U switch(char){ case’L’: long long_mem; case’c’: case’C’ char char_mem; default: long long_mem; }; 对应的C++类中还有一个用于存取每个联合成员的成员函数和一个用于修改每个联合成员的成员函数。此外,还有用于控制鉴别器和解决初始化和赋值问题的成员函数。映射如下:
联合的映射 Class U_var; Class U{ Public: U(); U(const U &); ~U(); U & operator=(const U &); CORBA::char _d() const; void _d(CORBA::Char); CORBA::Long long_mem() const; void long_mem(CORBA::Long); CORBA::char char_mem() const; void char_mem(CORBA::Char); const char * string_mem() const; void string_mem(char*); void string_mem(const char *); void string_mem(const CORBA::String_var &); Typedef U-var _var_type;}; 对于其它IDL类型,可能在类中还会有其它的成员函数。如果有的话,这些函数是映射实现的内部函数,可以不用考虑。
联合的映射 • 6.16.1 联合的初始化和赋值 1. U() 联合的缺省构造函数不会在应用程序中对类进行初始化,这就是说必须在读取联合的内容之前对联合进行初始化,甚至不允许读取一个缺省构造的联合的鉴别器的值。 2. U(const U &) U & operator=(const U &) 拷贝函数和赋值运算符进行多层次拷贝,因此如果联合中包含一个字符串,那么字符串内容就可以进行正确地拷贝。 3. ~U() 析构函数用于撤消一个联合。如果联合中包含了一个变长度成员,那么给成员函数分配地内容就可以正确地释放。撤消一个未初始化地缺省构造联合是安全的。
联合的映射 • 6.16.2 联合的成员与鉴别器的访问 为了激活一个联合成员或给一个联合成员赋值,可以调用相应的修改成员函数。给一个联合成员赋值也可以设置鉴别器的数值,可以通过调用_d成员函数读取鉴别器的数值。例如: U my_u; //没有初始化 my_u.long_mem(99); //激活long_mem assert(my_u._d()==‘L’); //验证鉴别器 assert(my_u.long_mem()==99); //验证值 以上没有在调用缺省的构造函数后对联合进行初始化。调用long_mem成员的修改成员函数可以对联合进行初始化,因为这样的话会激活该成员,并设置它的值。 为了改变联合中激活的成员,可以调用另外一个成员的修改函数,以给该成员赋值: my_u.char_mem(‘X’);//激活和分配给char_mem my_u._d(‘C’);//现在变成了‘C’
联合的映射 • 上面的代码首先激活成员char_mem,此激活成员可以将鉴别器设为‘c’或‘C’其中的一个,但是无法知道到底是那一个值。上述代码在激活成员后,将鉴别器的值明确设为‘C’。 • 如果设置鉴别器的值会激活或失效一个成员的话,那么就不能设置鉴别器的值: my_u.char_mem(‘X‘); //激活和分配char_mem assert(my_u._d()==‘c’||my_u._d()==‘C’); my_u._d(‘c’); //ok my_u._d(‘C’); //ok my_u._d(‘X’); //非法,将激活string_mem 只能将鉴别器设为与当前激活的联合成员一致的值(这里合法的值是‘c’和‘C’)。 设置联合的缺省成员可以使鉴别器设定未定义的状态。 上面的例子还说明。联合内部的字符串成员的作用类似于String_var。尤其是用于string_mem成员的修改成员函数是重载的,可以分别用于const char *、char *和String_var &。通常,char *修改函数拥有对字符串的所有权,而const char *和String_var修改函数进行多层次拷贝。
联合的映射 • 6.16.3 没有default语句的联合 下面是一个可以用来模拟可选参数的联合: union AgeOpt switch(boolean){ case TURE: unsigned short age;} 这一联合没有default语句,但是当鉴别器为FALSE时有一隐式缺省成员。如果联合有一个隐式缺省成员,那么映射将会生成一个附加的用于相应的C++类的_default成员函数。 class AgeOpt_var; class AgeOpt{ …… void _default(); typedef AgeOpt_var _var_type;}; 映射遵照一些常规的准则,也会添加_default成员函数。_default成员函数可以激活联合的隐式缺省成员,并且相应地设置鉴别器地值:
联合的映射 AgeOpt my_age(); my_age._default(); //设置鉴别器为假 在这种情况下,鉴别器的唯一合法的值是0(表示FALSE)。然而下面的语句是非法的: AgeOpt my_age; my_age._d(0); //非法 在此通过设置鉴别器来激活一个联合成员是非法的。(联合中不存在的隐式成员会被当作联合中的一个成员。) 同样,不能通过设置鉴别器的方法把一个已初始化的联合重新设置为缺省的成员,必须使用_default成员函数: AgeOpt my_age; my_age.age(38); //设置鉴别器为1 my_age._d(0); //非法 my_age._default(); //成功
联合的映射 • 下面是一个有趣的联合: enum HowManyProps{none,some;all}; union SpecifiedProps switch(HowmanyProps){ case some: ProperyNameSeq prop_names;}; 联合中允许两个非数值的鉴别器的值:none和all。假设对联合进行初始化,以将鉴别器的值设置为none,这里再次需要使用_default成员函数: SpecifiedProps sp; sp._default();//显式激活默认成员,鉴别器现在是none或者all sp._d(none);//固定鉴别器 必须要调用_default()。如果不调用_default的话,就需要设置鉴别器来激活隐含的缺省成员,可这是非法的。
联合的映射 • 6.14.4 包含复杂成员的联合 如果联合中包含了一个any类型的成员,或者包含了结构、联合、序列或定点数类型的成员,那么生成的类中将包含三个作用于每个联合成员的成员函数,而不是通常的两个成员函数。 struct Details{ double weight; long count;}; typedef sequence<string> TextSeq; union ShippingInfo switch(long){ case 0: Details packaging_info; default: TextSeq other_info;}; 其中一个是一个结构,另一个是一个序列。映射如下:
联合的映射 class ShippingInfo { public: const Details & dl_mem() const;//获得 void dl_mem(const Details &);//修改 Details & dl_mem();//引用 const TextSeq & seq_mem() const;//获得 void seq_mem(const Details &);//修改 TextSeq & seq_mem();//引用 }; 对于一些简单的类型,联合中包含了返回成员值的存取函数。(为了避免不必要的数据拷贝,作用于复杂类型的存取函数返回常量引用类型的数值),同时,对于简单的类型,每个成员函数都有一个进行多层次拷贝的修改函数。 引用成员函数返回一个对联合成员的非常量引用,使用这个成员函数可以提高效率。对于大的数据类型,通过调用存取函数和修改函数对函数进行修改的效率很低,因为这两个函数都进行多层次拷贝。通过引用就可以修改联合成员的值,而不用进行拷贝。
联合的映射 • 如果获得一个对联合成员的引用,这个成员必须是当前和÷激活的。一旦有了一个对联合成员的引用,必须在相应成员处于激活状态时才能使用这个成员。 • 6.16.5 下面时安全使用联合的一些规则: (1) 不要试图访问一个与鉴别器不一致的联合成员。 (2) 不要假定联合成员在内存中会重叠。在C和C++中,联合成员在内存中可以彼此重叠。然而,在IDL联合的C++映射并不提供这一功能。 (3) 不要猜测什么时候会调用析构函数。C++映射并不指明什么时候应该撤消联合的成员。如果激活一个新的成员,那么可能会因为效率的原因而推迟调用先前成员的析构函数。
6.17 递归结构和递归联合的映射 • 考虑下面的递归联合: union Link switch(long){ case 0: typeA ta; case 1: typeB tb; case 2: sequence<Link> sc;}; 上面的联合中包含一个递归成员sc。假设想要激活联合中的sc成员,使得为一个空序列。激活一个联合成员的唯一方法就是向存取器传递该成员的值,然而,sc是一个匿名类型,那么,如何来说明一个这样类型的变量。 C++映射可以通过在联合类中生成一个附加的类型定义来解决这一问题: calss Link{ public: typedef some_internal_identifier_sc_seq; //别的成员};
递归结构和递归联合的映射 • 生成类中定义了一个_sc_seq类型名称来表示匿名类型。总的来说,如果联合u中包含了一个匿名类型的mem成员,那么mem的类型就是u::_mem_seq。可以使用这个名称来正确地激活联合中地递归成员: Link::_sc_seq myseq;//myseq是空的 Link mylink; mylink.sc(myseq)//激活sc 这一映射规则也可以用于递归地结构。如果一个结构s中包含了一个匿名成员mem,那么mem地类型名就是s::_mem_seq。 6.18 类型定义的映射 IDL类型定义可以映射为C++中对于的类型定义。类型的别名可以与初始类型一样使用。
6.19 用户定义类型和_var类 • 6.19.1 用于结构、联合和序列的_var类 class T_car{ …… }; 解释如下: 1. T_var(); 缺省的构造函数将指向当前使用的实例的内部指针初始化为空指针。这样做的结果是在初始化缺省构造的_var实例之前,不能使用它。 2. T_var(T *) 指针构造函数假定传递的指针指向一个动态分配的实例,并且拥有对指针的所有权。 3. T_var(const T_var &) 拷贝构造函数对T_var和T类型的当前使用实例进行多层次拷贝。 4. ~T_var() 析构函数释放由内部指针所指向的实例。
用户定义类型和_var类 • 5. T_var & operator=(T *) 指针赋值运算符首先释放当前由T_var拥有的类型T的实例,然后承担由参数所指的实例的所有权。 6. T_var & operator=(const T_var &) T_var赋值运算符首先释放当前由T_var拥有的类型T的实例,然后对T_var参数和T_var参数所指向的类型T的实例进行多层次拷贝。 7. T * operator->() const T * operator->() const 重载间接运算符是为了可以让运算符用于常量和当前使用类型的非常量实例。运算符返回一个指向当前使用实例的指针。这意味着,可以通过T_var来调用基本类型的任何成员函数。 8. operator T &() const operator T &() const 通过这些转换运算符,T_var可以用于需要用到对当前使用类型的常量或非常量用于的地方。 9 T & operator[](CORBA::ULong) const T & operator[](CORBA::ULong)const 如果T_var表示一个序列或数组,那么就会生成下标运算符。
用户定义类型和_var类 • 6.19.2 _var类的简单使用 以下是一个序列的定义: typedef sequence<string> NameSeq; 这样将会生成两个C++类型:NameSeq,这是一个真正的序列,以及NameSeq_var,这是一个对应的内存管理封装函数。 对于String_var,生成的_var类型只是用于为动态分配的变长度类型获取返回值。例如: extern NameSeq * get_name();//返回分配的堆内存句柄 NameSeq_var nsv=get_name();//nsv获得所有权 //不需要考虑在此释放 6.19.3 使用_var类的一些缺陷 使用String_var需要注意的地方同样也适用与_var类。如果用一个指针对_var实例进行初始化或对一个指针进行赋值,那么需要确认指针确实三÷指向动态分配的内存,否则就会导致灾难性后果。
用户定义类型和_var类 • 同时,将一个指针赋给_var实例后,在间接引用该指针时必须加以小心。因为对_var的赋值将会释放前面使用的实例,因此会使指向该实例的指针失效。 • 6.19.4 定长度和变长度的结构、联合和序列之间的区别 在变长度类型中: 转换运算符可以用来把一个变长度的T_var传递给需要用到对指向T的指针的引用的地方。其中的成员函数可以用来将T_var作为in、inout或out参数来传递,而不是缺省的类型的转换。 out成员函数释放当前使用的类型T的实例,以避免因为相同的T_var实例连续传递给函数调用而产生内存泄漏。 _retn()函数指向当前的类型的T的实例,并放弃对指针的所有权。它主要用于创建一个T_var实例以避免内存的泄漏,然后又必须传递对当前使用类型的所有权的情况。 在定长度类型中: 附加的构造函数和赋值运算符可以用来构造T_var,或把T赋给T_var。其中的成员函数可以用于不能正确处理缺省类型转换的编译器,同时,可以提高代码的执行效率。 用于定长度类型的out和_retn成员函数不会放弃对当前使用类型的所用权,不能这样,是因为它们不返回一个指针。
用户定义类型和_var类 • 6.19.5 数组的_var类型 数组的_var类型与结构、联合和序列的_var类型具有相同的形式,区别是数组的_var类型并不重载间接运算符,并且一些成员函数的返回值类型也有所不同,变长和定长也不同。 包含变长元素的数组的_var映射 其中的成员函数与结构、联合和序列的_var类型中的成员函数的作用完全相同。 数组映射的说明 (1) 缺省的构造函数将指向当前使用数组的内部指针初始化为空指针 (2) 参数为F_slice *的构造函数和赋值运算符都假定由F_alloc或F_dup对数组分配内存,并且它们拥有对传递来的指针的所有权。 (3) 拷贝构造函数和F_var &赋值运算符都进行多层次拷贝
用户定义类型和_var类 (4) 析构函数通过调用F_free来释放数组 (5) 下标运算符允许使用数组下标,这样,F_var就可以当作数组来使用 (6) 通过转换运算符可以将数组作为in、inout或out参数来传递 (7) 显示转换函数in、inout和out与结构、联合和序列中的使用方法一样 (8) 通过_retn函数可以放弃对当前使用类型的所用权 所有这些意味着,可以将_var数组当作正真的数组来使用,只是_var数组必须通过动态分配内存来进行初始化。
用户定义类型和_var类 • 包含定长元素的数组的_var映射 包含定长元素的数组和包含变长元素的数组的_var例子之间的区别是对于定长度元素,inout成员函数返回一个指针,而不是对指针的引用,并且没有定义用于StructArray_slice * &的用户定义的转换运算符。这些区别由变长和定长在参数传递上的不同所产生。