WEB开发网      婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈功缁犻箖鏌嶈閸撴氨鎹㈠☉娆愬闁告劕寮堕幖鎰棯閸撗勫殌闁宠鍨块幃鈺冣偓鍦Т椤ユ繈姊哄Ч鍥р偓妤呭磻閹捐桅闁告洦鍨扮粻娑㈡煕椤愶絾绀冩い搴$Ч濮婅櫣绮欏▎鎯у壋闂佸摜濮甸崝娆愪繆閻㈢ǹ绀嬫い鏍ㄨ壘閸炪劑姊洪棃娴ゆ稒鎷呴幓鎺嶅闂佸湱鍎ら〃鍡涘煕閹烘鐓曢柡鍥ュ妼娴滄粍銇勮箛锝呭籍闁哄备鈧磭鏆嗛悗锝庡墰閺嗙娀鏌ф导娆戝埌闁靛棙甯掗~婵嬫偂鎼达絼鐢荤紓浣诡殕閸ㄥ灝顫忕紒妯诲缂佹稑顑呭▓顓炩攽椤旀枻鍏紒鐘虫崌閵嗕礁顫濋幇浣光枌婵犵數濮崑鎾趁归敐鍥┿€婇柡鈧禒瀣厽婵☆垱顑欓崵瀣偓瑙勬偠閸庤精鐏冮梺缁樏鍫曞疮閻愮數纾奸柛灞炬皑鏁堥悗瑙勬礃缁繘藝鐎靛摜妫柟顖嗕礁浠悗娈垮枛閻栫厧鐣烽悡搴樻婵☆垯璀﹂悗宕囩磽閸屾瑧鍔嶆い銊ユ閻f繈骞栨担姝屾憰闂佺粯妫冮ˉ鎾诲汲鐎n喗鐓熸俊銈傚亾闁绘妫楅埢鎾澄旈崨顔规嫼闁荤姴娲犻埀顒冩珪閻忊偓闂備礁鎼幊鎰叏閹绢喗鍋╅柣銈庡灛娴滃綊鏌熼悜妯肩畺闁哄懏绻堝娲濞戞艾顣哄┑鈽嗗亝閻熲晠銆佸▎鎺旂杸闁哄啫鍊婚惁鍫ユ⒑濮瑰洤鐏叉繛浣冲嫮顩烽柨鏇炲€归悡鏇㈡煏婵炲灝鍔ら柛鈺嬬稻椤ㄣ儵鎮欓弶鎴濐潚濡ょ姷鍋為敃銏ゃ€佸▎鎾村殐闁冲搫顑囬獮銏ゆ⒒閸屾瑦绁版い顐㈩槸閻e嘲螣閼测晝鐓嬪銈嗘閿熴儲绂嶈ぐ鎺撶厵闁绘垶蓱鐏忣厼霉濠婂啰绉烘慨濠呮缁辨帒螣閾忛€涙闂備焦瀵уú宥夊疾濞戞粎浜遍梻浣告啞濞诧箓宕归柆宥呯厱闁硅揪闄勯悡娆撴煠濞村娅呭ù鐘崇矊閳规垿鍨鹃悙钘変划闂佽鍠楅〃鍛村煡婢舵劕绠抽柟鎯ь嚟瑜板洨绱撻崒娆戣窗闁哥姵鐗犻、鏍川閹碱厽鏅i梺绋跨箳閸樠呮閻愮繝绻嗘い鏍ㄧ矌鐢稒绻涢崨顓熷枠婵﹦绮幏鍛存偡闁箑娈濈紓鍌欐祰椤曆囧磹閸噮鍤曠紓浣贯缚缁♀偓闂佹悶鍎崝宥呪枍閸ヮ剚鈷戠紒瀣濠€鎵磼鐎n偅宕岀€规洏鍨介幃浠嬪川婵犲嫬骞楅梺鐟板悑閻n亪宕规繝姘厐闁哄洢鍨洪悡銉︽叏濡灝鐓愰柣鎾跺枛閻擃偊宕堕妷銉ュБ缂備礁顑堝畷鐢垫閹烘梻纾兼俊顖濆亹閻h櫣绱撴担铏瑰笡缂佽鐗嗛悾宄邦潨閳ь剚淇婂宀婃Ш缂備浇椴哥换鍫濐潖缂佹ɑ濯寸紒娑橆儏濞堟劙姊洪幖鐐插闁告鍟块悾鐑筋敍閻愯尙楠囬梺鐟邦嚟婵潧鈻撴ィ鍐┾拺缂備焦蓱閳锋帡鏌嶅畡鎵ⅵ鐎殿噮鍋婂畷鎺楁倷鐎电ǹ骞堥梻浣瑰▕閺侇噣宕戦幘缁樼厸闁告侗鍠氶幊鍛繆閸欏濮囬摶锝夋偠濞戞帒澧查柡鍌楀亾闂傚倷鑳剁划顖炲礉閺囩倣鐔哥節閸パ冩優闂佺粯鏌ㄩ惃婵嬪绩閼恒儯浜滈柡鍐ㄦ处椤ュ鏌涢弬璇测偓婵嬪箺閸洘鍊烽柣鎴炨缚閸橀亶姊洪崫鍕偍闁告柨鏈弲鍫曨敍閻愬鍘卞┑鐐叉缁绘帞绮绘繝姘厸閻忕偟鏅晥閻庤娲﹂崑濠傜暦閻旂⒈鏁嗛柍褜鍓欓埢宥夋晲閸モ晝锛濇繛杈剧稻瑜板啯绂嶉悙顒傜瘈闁靛骏绲剧涵鐐亜閹存繃宸濈紒顔剧帛閵堬綁宕橀埡鍐ㄥ箥闂佽瀛╃粙鎺戠幓鐠恒劎涓嶆慨妞诲亾闁哄被鍔岄埥澶娢熸径鐧哥稻閵囧嫰濡搁敐鍛Е闂佽鍠楅悷鈺呫€侀弮鍫濈妞ゆ挻绻勭粈鍕⒒閸屾瑦绁版い鏇熺墵瀹曚即寮介銈囶槸婵犵數濮撮崐濠氬汲閿曞倹鐓欐い鏍仜娴滅増淇婇懠棰濆殭闁宠鍨块崺鍕礃閵娧呫偡婵$偑鍊ら崢楣冨礂濡警鍤曢悹鍥ㄧゴ濡插牓鏌曡箛鏇烆潔闁冲搫鎳忛悡蹇擃熆鐠鸿櫣澧曢柛鏃€鎸抽弻娑㈠棘濞嗙偓楔缂備浇椴搁幐濠氬箯閸涱垳鐭欓幖瀛樻尭娴滈箖鏌涘┑鍕姢闁活厽鎸鹃幉鎼佹偋閸繄鐟ㄩ梺鍝勵儎缁舵岸寮婚悢鐓庣鐟滃繒鏁☉銏$厸闁告侗鍠楅崐鎰版煛鐏炶濮傞柟顔哄€濆畷鎺戔槈濮楀棔绱� ---闂傚倸鍊搁崐鎼佸磹閹间礁纾归柣鎴eГ閸婂潡鏌ㄩ弮鍫熸殰闁稿鎸剧划顓炩槈濡搫绠诲┑鐐叉▕娴滄粓鎮″☉銏$厱婵炴垵宕獮妯汇亜閺傛寧顥㈡慨濠呮閹瑰嫰濡搁妷锔惧綒闂備胶鎳撻崵鏍箯閿燂拷
开发学院软件开发C++ 实例解析C++/CLI之值类型 阅读

实例解析C++/CLI之值类型

 2008-03-08 12:39:47 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣椤愯姤鎱ㄥ鍡楀幊缂傚倹姘ㄩ幉绋款吋閸澀缃曢梻鍌欑濠€閬嶆惞鎼淬劌绐楅柡宥庡亞娑撳秵銇勯弽顐沪闁绘挶鍎甸弻锝夊即閻愭祴鍋撻崷顓涘亾濮樼偓瀚�闂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣捣閻棗銆掑锝呬壕濡ょ姷鍋涢ˇ鐢稿极閹剧粯鍋愰柟缁樺笧閳ь剦鍙冨鍝勑ч崶褏浠奸梺璇茬箲閼归箖鎮鹃悜钘夎摕闁靛濡囬崢鐢告⒑鐟欏嫷鍟忛柛鐘崇墵閵嗗倹绺介崨濠勫幈闁硅壈鎻槐鏇熺墡闂備線娼уú銈団偓姘嵆閻涱噣骞掑Δ鈧粻锝嗙節闂堟稑鏆欏ù婊堢畺閺岋綁濮€閳惰泛婀辨竟鏇熺節濮橆厾鍘甸梺缁樺姦閸撴岸鎮樻潏銊ょ箚闁圭粯甯炴晶娑氱磼缂佹ḿ娲寸€规洖宕灃闁告劕鍟犻崜婵堟崲濞戞ḿ鏆嗗┑鐘辫兌閺佹牜绱撴担浠嬪摵闁圭懓娲ら悾鐑藉箳閹搭厽鍍甸梺鐟板悁閻掞箓鎮楅幖浣光拻濞达絿鍎ら崵鈧梺鎼炲€栭悧鐘荤嵁韫囨稒鏅搁柨鐕傛嫹婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繑銇勯幘鍗炵仼缂佺媭鍨堕弻娑㈠箛闂堟稒鐏堥悗鐟版啞缁诲啴濡甸崟顖氱閻庨潧鎽滈悾濂告⒑绾拋娼愭繛鑼枎椤繒绱掑Ο鑲╂嚌闂侀€炲苯澧畝锝堝劵椤︽煡鎮¢妶澶嬬厪闁割偅绻冮崑顏呯箾瀹割喕绨婚幆鐔兼⒑鐎圭姵銆冮柤鍐茬埣瀹曟繈鏁冮埀顒勨€旈崘顔嘉ч柛鈩冾殘閻熸劙姊洪悡搴℃毐闁绘牕銈稿畷鐑樼節閸パ冨祮闂侀潧楠忕槐鏇㈠储椤忓牊鈷戦柟鑲╁仜閸旀鏌¢崨顔锯姇缂佸倹甯熼ˇ瀵哥磼鏉堛劌绗氭繛鐓庣箻閸┾剝鎷呴柨瀣垫綗闂傚倷娴囧銊╂倿閿曞倸绠查柛銉墮閺嬩線鏌熼崜褏甯涢柡鍛倐閺屻劑鎮ら崒娑橆伓闂傚倸鍊搁崐鎼佸磹閹间礁纾瑰瀣椤愯姤鎱ㄥ鍡楀幊缂傚倹姘ㄩ幉绋款吋閸澀缃曢梻鍌欑濠€閬嶆惞鎼淬劌绐楅柡宥庡亞娑撳秵銇勯弽顐沪闁绘挶鍎甸弻锝夊即閻愭祴鍋撻崷顓涘亾濮樼偓瀚�  闂傚倸鍊搁崐鎼佸磹閹间礁纾归柣鎴eГ閸ゅ嫰鏌ら崫銉︽毄濞寸姵姘ㄧ槐鎾诲磼濞嗘帒鍘$紓渚囧櫘閸ㄥ爼濡撮崘顔煎窛闁哄鍨归崢娲倵楠炲灝鍔氭い锔诲灦瀹曪繝骞庨懞銉у帾闂婎偄娲﹀ú鏍ㄧ墡闂備浇顕х€垫帡宕滈悢濂夋綎闁惧繐婀辩壕鍏间繆椤栨碍鎯堟い顐㈢焸濮婅櫣鎷犻懠顒傤唹濠殿喗菧閸旀垿宕洪埀顒併亜閹哄秶顦﹂柛銈庡墴閺屾盯骞樼捄鐑樼€诲銈庡亜缁绘劗鍙呭銈呯箰鐎氼剟鎮楅鐑嗘富闁靛牆妫欑粈鈧梺鐟板暱闁帮絽鐣峰⿰鍕嚤閻庢稒菤閹锋椽姊绘笟鍥т簽闁稿鐩幊鐔碱敍濞戞瑦鐝峰銈嗘煥婢х晫澹曢悡搴唵閻犺櫣灏ㄩ崝鐔虹磼婢跺孩顏犻柍褜鍓氶鏍窗閺嶎厸鈧箓鏌ㄧ€b晝绠氬┑顔界箓閻牆危閻戣姤鈷戠紒瀣儥閸庢劙鏌熼悷鐗堟悙閾荤偤鏌涢幇鈺佸Ψ婵℃彃鐗婄换娑㈠幢濡ゅ啰顔夊┑鐐茬墛閿曘垹顫忕紒妯诲濡炲绨肩憰鍡欑磽閸屾氨袦闁稿鎸荤换娑氣偓娑欋缚閻倝鏌涢幘璺烘灈鐎规洘妞介崺鈧い鎺嶉檷娴滄粓鏌熼悜妯虹仴闁逞屽墮缂嶅﹤顕i幎绛嬫晢闁告洦鍓涢崢閬嶆煟鎼搭垳绉靛ù婊呭厴閻擃剟顢楅崒妤€浜鹃悷娆忓绾惧鏌涘Δ鈧崯鍧楊敋閿濆纾归柣鏇氱劍闉嬮梻鍌欑閹碱偄螞鐎靛摜涓嶉柟鎹愵嚙閽冪喖鏌曟繛鐐珕闁稿妫濋弻娑氫沪閸撗€妲堝銈呴獜閹凤拷
核心提示:值类型是一种轻量级的C++/CLI类机制,非常适合于小型的数据结构,实例解析C++/CLI之值类型,且从语义的角度来看,与数值(Value)类似,清理释放值类型自身所占用的内存可不是一件简单的事情,因为每种类型的数据成员在超出作用域时,与之相比,引用类型的实例--包括那些声明在堆栈上的

  值类型是一种轻量级的C++/CLI类机制,非常适合于小型的数据结构,且从语义的角度来看,与数值(Value)类似。

  与之相比,引用类型的实例--包括那些声明在堆栈上的,是由垃圾回收器治理的,而值类型的实例却不是。一般来说,一个值类较好的实现应只有一些数据成员,而不需要继续性,这样,在函数传递及返回值、或是赋值操作时,不会带来巨大的数据开销。

  值类初印像

  请看例1中的Point类,可以通过替换ref为value,来把一个引用类变为值类;与引用类(ref)相似,值类(value)也是一个包含了空格的要害字。与大家想像的一样,值类(value)与值结构(value strUCt)之间唯一的区别就是,前者默认的可访问性为PRivate,而后者则为public。

  例1:

using namespace System;
public value class Point
{
 int x;
 int y;
 public:
  //定义属性X与 Y的读写实例
  property int X
  {
   int get() { return x; }
   void set(int val) { x = val; }
  }
  property int Y
  {
   int get() { return y; }
   void set(int val) { y = val; }
  }
  //定义实例构造函数
 
  Point(int xor, int yor)
  {
   X = xor;
   Y = yor;
  }
  void Move(int xor, int yor)
  {
   X = xor;
   Y = yor;
 }
 virtual bool Equals(Object^ obj) override
 {
  if (obj == nullptr)
  {
   return false;
  }
  if (GetType() == obj->GetType())
  {
   Point^ p = static_cast<Point^>(obj);
   return (X == p->X) && (Y == p->Y);
  }
  return false;
 }
 static bool Operator==(Point p1, Point p2)
 {
  return (p1.X == p2.X) && (p1.Y == p2.Y);
 }
 // static bool operator==(Point% p1, Point% p2)
 // {
 // return (p1.X == p2.X) && (p1.Y == p2.Y);
 // }

 // static bool operator==(Point& p1, Point& p2)
 // {
 // return (p1.X == p2.X) && (p1.Y == p2.Y);
 // }
 virtual int GetHashCode() override
 {
  return X ^ (Y << 1);
 }
 virtual String^ ToString() override
 {
  return String::Concat("(", X, ",", Y, ")");
 }
};
  值类自动继续自System::ValueType,而System::ValueType则继续自System::Object,但是,这却不能显式地声明。值类隐式表明了为"sealed",也就是说,它不能被作为一个基类,另外,为其类成员指定一个protected是没有任何意义,并且也是不答应的。假如想显式声明一个值类(或引用类),可像如下所示:

value class X sealed {/*...*/};
  请注重,此处没有默认的构造函数。对一个值类来说,CLI本身把类实例中所有字段的位都设置为零,所以,不能提供自己的默认构造函数;然而,零、false、nullptr对其他类型来说,也许并不是合适的默认值,因此,对某些特定类型来说,就要用引用类型来取代值类型了。(遵从C++/CLI的实现会将false与nullptr表示为位全部为零。)

  值类的另一个限制是它们带有一个默认的拷贝构造函数和一个赋值操作符,两者都会进行逐位复制,并不可被重载。

  假如要实现Point类中的Equals函数,相比引用类中的而言要简单一些。请记住,我们正在重载定义System::Object中的这个版本,而其接受一个Object^,因为这种类型的参数很可能有一个nullptr值,在此,先可以省去检查是否为自身比较这一步,而对引用类的Equals实现来说,这一步是必需的,因为可有多个句柄引用同一对象。但是话说回来,在目前的这个值类中,没有两个值的实例可表示同一个实例,两个相同的值实例,只代表两个Point有相同的坐标,但修改其中一者的x坐标,不会影响到另一者的相同值。

  当一个Point的实例传递到Equals时,作为值类型(其最终也都继续自System::Object)而言,装箱就发生了--也就是说,在垃圾回收堆上分配了一个Object的实例,而其包含了传递进来Point的一份副本。因为是创建了一个新的对象,所以只有一个句柄,也不会有相同的其他Point。

  之前接受Point句柄的 == 操作符函数,现在已经精简到一行,并且由接受句柄改为接受Point值,且用于选择成员的指向操作符 -> 也被替换为点操作符。因为给定的值类型为sealed,所以与值类型参数Point唯一匹配的则为同类型的值了。同样地,既无需检查nullptr来确认是否为自身比较,也无需检查传递进来的对象是否类型完全一致。

  而之前用于追踪引用的 == 操作符函数基本上无需太多改动,但删除了检测同一类型这一部分。然而,这两个== 操作符函数,最好只保留一个,以免在point1 == point2调用时引发歧义。(在声明函数参数时,也可使用标准C++引用符&,而不是%,因为两者可在本地类型与值类型之间互换。但由于这种类型的实例不存在于垃圾回收堆中,所以在垃圾回收期间不会改变它们的位置,因此也不需要对它们的位置进行追踪。)

  例2使用了值类中的大多数成员,最主要的是它包含了静态Point类的实例,而这在引用类中是不可能完成的。事实上,不只是不能有一个引用类的静态实例,甚至也不能有一个此类型的静态句柄。

  例2:


using namespace System;

Point p1;
static Point p2(3,4);

int main()
{
 static Point p3(4,7);

 Console::WriteLine("p2 is {0}", p2);
 Point% p4 = p3;

 Point p5 = p2;
 p5 = p2;

 Console::WriteLine("p1 == p2 is {0}", p1 == p2);
 Console::WriteLine("p1.Equals(p2) is {0}", p1.Equals(p2));
}
p2 is (3,4)
p1 == p2 is False
p1.Equals(p2) is False
  在第一次调用Console::WriteLine时,用传值的方式传递进一个Point,但是,这个函数却指望着接受一个对象引用,在此,Point值被自动装箱,并把装箱后的对象引用传递给函数。
  
  在定义中可看到,p5是由默认的拷贝构造函数初始化,而接下来的一行代码,默认的赋值操作符把p2逐位复制给p5。 引用类与值类的差异

  假如我们在上述的Point引用类中加入一个ID号,用于跟踪每个不同的Point引用对象,且再添加一个布尔类型的TraceID用于指明是否进行跟踪;那么,把它改为值类之后,会有什么不同呢?

  再次提醒,是不能为一个值类定义默认构造函数、拷贝构造函数及赋值操作符的,但不幸的是,这些都是我们ID解决方案中所需用到的。在引用类版本的默认构造函数中,会将X与Y两个坐标值、ID值都设置为零,并取得下一个ID赋给ID实例字段;反观值类实现的版本,对以此方式构建的每个新Point,都是由默认为零值的ID构成,但是,我们却想每个ID值为唯一。
 
  另一个类似问题也是由缺少显式的拷贝构造函数造成的,在我们想要一个全新的对象时,值类的逐位复制却造成新对象的ID与被拷贝对象的ID一样。

  另外,在赋值时,假如我们只设置即有Point的值,那么Point的ID不应改变,也就是说,虽然任一或两个坐标都可能改变,但它仍是同一Point对象,然而,逐位复制却导致目标Point的ID被源对象ID覆盖。

  虽然此处没有列出包含ID的Point类,但例3中的程序显示了引用类与值类的差异所在。

  例3:

using namespace System;

int main()
{
 Point::TraceID = true;

 Point p1, p2(3,7), p3(9,1), p4 = p2;
 Console::WriteLine("p1 = {0}", p1);
 Console::WriteLine("p2 = {0}", p2);
 Console::WriteLine("p3 = {0}", p3);
 Console::WriteLine("p4 = {0}", p4);
 p2 = p1;
 Console::WriteLine("p2 = {0}", p2);
}
  第一次运行后,4个Point的输出如下:

Point p1, p2(3,7), p3(9,1), p4 = p2;
p1 = [0](0,0)
p2 = [0](3,7)
p3 = [1](9,1)
p4 = [0](3,7)
  Point p1由默认构造函数创建,它的ID为零,但却恰好也是第一个Point的正确ID值,默认的坐标值也为零。而p2用到了自己编写的构造函数,其分配了一个可用的ID,也就是零,这样,我们有了两个一样的ID。

  同样地,p3得到了ID值1,接下来,把p2逐位复制给p4,p4的ID与p2相同。在执行p2 = p1逐位复制之后,p1与p2两个对象都有了相同的p1的ID。

  程序第二次运行后,输出如下:

p1 = [0](0,0)
p2 = [2](3,7)
p3 = [3](9,1)
p4 = [2](3,7)
p2 = [0](0,0)
  在此可看到,p1的ID值总为零。

  显而易见,引用类与值类是各有千秋,不是在每种场合,都可以调换使用的。 基本类型映射

  遵照标准C++的精神,对CLI值类型的基本类型映射,都已经全部在定义中实现了,就Microsoft Visual C++而言,映射关系如表1所示。

C++/CLI类型 CLI值类型 bool System::Boolean wchar_t System::Char signed char System::SByte unsigned char System::Byte char System::SByte或 System::Byte short int System::Int16 unsigned short int System::UInt16 int System::Int32 unsigned int System::UInt32 long long int System::Int64 unsigned long long int System::UInt64 float System::Single double System::Double    表1:C++/CLI与CLI值类的映射关系

  另外,还有一种值类型:System::Decimal,但没有对应的C++/CLI类型。

  请看以下表达式,它们都涉及到访问前述CLI值类型的静态或实例成员。

Int32::MaxValue
Double::Parse("123.45e-1")
10.2f.ToString()
(10 + 5.9).ToString()
(100).ToString()
100 .ToString()
  因应Visual C++的映射,10.2f的类型为float,其映射为System::Single,并调用了其ToString函数;类似地,(10 + 5.9)类型为double,因此调用了System::Double的ToString。显然,从语义的角度来看,带有圆括号的100与其后带有一个空格的100,这种写法是多余的,但是,假如忽略它们,100与其后的句点将会解析为一个带有标识符的double常量,这会导致语法错误。

  复数问题

  例4,演示了一个有着实部与虚部的复数的值类型。

  例4:


using namespace System;
public value class Complex
{
 double real;
 double imag;
 public:
  static initonly Complex i;
  static Complex()
  {
   i = Complex(0.0, 1.0);
  }
  Complex(double real)
  {
   this->real = real;
   this->imag = 0.0;
  }
  Complex(double real, double imag)
  {
   this->real = real;
   this->imag = imag;
  }
  property double Real
  {
   double get() { return real; }
   void set(double value) { real = value; }
  }
  property double Imag
  {
   double get() { return imag; }
   void set(double value) { imag = value; }
  }
  static Complex operator+(Complex z1, Complex z2)
  {
   return Complex(z1.real + z2.real, z1.imag + z2.imag);
  }
  static Complex operator-(Complex z1, Complex z2)
  {
   return Complex(z1.real - z2.real, z1.imag - z2.imag);
  }
  String^ ToString() override  
  {
   if (imag < 0.0)
   {
    return String::Format("({0} - {1}i)", real, -imag);
   }
   else if (1.0/imag == Double::NegativeInfinity)
   {
    return String::Format("({0} - 0.0i)", real);
   }
   else
   {
    return String::Format("({0} + {1}i)", real, +imag);
   }
  }
 };                                                                         
  CLI要求使用IEEE浮点表示法,这是一种比IEC 10559更正式的表示法,其中,零在single与double中表示为全部位为零。正因为此,所以可安全地使用CLI提供的默认构造值。

  程序中,定义了一个复数i,其表示-1的平方根,这样,复数类型就可以提供具有此值的public只读常量,而这是由一个public static成员及一个static构造函数共同完成的。因为Complex在此不是一个基本类型,所以i不能成为一个只读(readonly)成员,因为无论如何,这都需要用一个常量表达式来初始化它,但这种事是不存在的。所以,我们能做的,就是让i成为initonly,并在static构造函数中初始化它。例5是测试程序及输出。

  例5:

using namespace System;
int main()
{
 Complex c1;
 Complex c2(12.5);
 Complex c3(-1.23, -4.5);
 Complex c4 = c2 + c3;
 Complex c5 = c2 - c3;

 Console::WriteLine("c1: {0}", c1);
 Console::WriteLine("c2: {0}", c2);
 Console::WriteLine("c3: {0}", c3);
 Console::WriteLine("c4: {0}", c4);
 Console::WriteLine("c5: {0}", c5);
 Console::WriteLine("i: {0}", Complex::i);
 Console::WriteLine("c3.Real: {0}", c3.Real);
 Console::WriteLine("c3.Imag: {0}", c3.Imag);
}

c1: (0 + 0i)
c2: (12.5 + 0i)
c3: (-1.23 - 4.5i)
c4: (11.27 - 4.5i)
c5: (13.73 + 4.5i)
i: (0 + 1i)
c3.Real: -1.23
c3.Imag: -4.5
  一些其他事项
  
  注重,一个值类型不应包含:

  ·类型为本地C++数组、本地类类型或位字段的数据成员

  ·包含局部类的成员函数

  ·为friend的成员

  ·析构函数

  一个传值、传址、传引用、或追踪引用的函数,可传递进或返回一个值类。

  在引用类T的实例构造函数或成员函数中,this的类型为"指向T的句柄",然而,对值类型而言,this为interior_ptr<T>。
 
像Point与Complex这样的简单值类型实例是完全自我包含的--但却不是必须的,举例来说,与引用类型相似,一个值类型也能包含指向本地堆的指针及垃圾回收堆中对象的句柄。在这种情况下,清理释放值类型自身所占用的内存可不是一件简单的事情,因为每种类型的数据成员在超出作用域时,都需要进行清理。

Tags:实例 解析 CLI

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接