知识大全 实例解析C++/CLI之静态构造函数

Posted 函数

篇首语:生活就是这样,别人看结果,自己撑过程。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 实例解析C++/CLI之静态构造函数相关的知识,希望对你有一定的参考价值。

实例解析C++/CLI之静态构造函数  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

就某些类而言 当在程序中第一次使用时 最好能有一个初始化过程 当程序不再需要时 也最好能做一些收尾工作 这些都是非常好的类设计习惯    引出问题  如果有这样一种情况 某种类型的每个实例都必须有其唯一的ID 比如说某种交易类型 这些ID可用于在处理过程中追踪每笔交易 或之后用于审计员查看数据文件 为讨论方便 此处的ID为从 起始的有符号整型数     如果把一个nextID值保存在内存中 并在每个新实例构造时 把它递增 这无疑是一个不错的想法 但是 为使在程序连续的执行过程中保持ID值的唯一 就需要在每次程序结束时保存此值 并在下次程序开始运行时恢复这个值 但在标准C++中 是没办法来达到这个目的的 实际上 使用标准CLI库也同样没办法完成 然而 在CLI的 NET实现中有几个扩展库 它们却可以完成这个任务    问题重现  这回又用到了Point类 因为带有唯一ID的点很适合此主题 例 中的程序输出在代码之后   例 using namespace System;Point F(Point p)  return p;int main() /* */ Point::TraceID = true; /* */ Point^ hp = gcnew Point; Console::WriteLine( hp : hp ); /* */ hp >Move( ); Console::WriteLine( hp : hp ); /* */ Point^ hp = gcnew Point( ); Console::WriteLine( hp : hp ); /* */ Point p p ( ); Console::WriteLine( p : p : %p %p ); /* */ p = F(p ); Console::WriteLine( p : %p );  输出 hp : [ ]( )hp : [ ]( )hp : [ ]( )p : [ ]( ) p : [ ]( )p : [ ]( )  在程序开始运行时 从一个文本文件中读取下一个可用的ID值 并用它来初始化一个Point类中的私有静态(private static)字段 最开始 这个文件包含的值为零   基于公共静态布尔属性TraceID的值 Point中ToString函数生成的字符串可有选择地包含Point的ID 并以 [id] 的形式作为一个前缀 如果此属性值为true 就包含ID前缀 否则 就不包含 默认情况下 这个属性值被设为false 因此 在标号 中我们把它设为true   在标号 中 使用默认构造函数为Point分配了内存空间 并显示它的ID为 及值为( ) 在标号 中 通过Move函数修改了Point的x与y坐标值 但这不会修改Point的ID 毕竟 它仍是同一个实例 只不过用了不同的值 接着 在标号 中 使用了接受两个参数的构造函数为另一个Point分配了内存空间 并显示它的ID为 及值为( )   在标号 中创建了两个基于堆栈的实例 并显示出它们的ID及值 在第三个及第四个Point创建时 它们的ID分别为 和   在标号 中 p 被赋于了一个新值 然而 p 仍是它之前的同一个Point 所以它的ID没有改变   第二次运行程序时 输出如下 hp : [ ]( )hp : [ ]( )hp : [ ]( )p : [ ]( ) p : [ ]( )p : [ ]( )

  如上所示 个新实例都被赋于了连续的ID值 且与第一次执行时截然不同 但是 还缺少ID 和 请留意标号 及函数F的定义 Point参数是传值到此函数的 而一个Point也是通过值返回的 同样地 这两者都会调用到复制构造函数 而其则 忠实 地创建了一个新实例 且每个新实例都有一个唯一的ID 因此 当p 通过值传递时 会创建一个ID为 的临时Point 紧接着 当副本通过值返回时 又会创建一个ID为 的副本 而两个副本都是可丢弃的 当程序结束时 写入到文件中下一个可用的ID为 而在程序下次运行时 这就是第一个Point在分配空间时将用到的ID

  解决方法  例 中为Point类的修订版本 非常明显 每个实例现在必须包含一个额外的字段(在此为ID) 用以保存ID 在此选择的类型为int 虽然标准C++允许其最小为 位 但在CLI环境中 其至少为 位 如果以零开始 那么在ID重复之前 能表示 亿个不同的实例 当然 也能以负 亿开始 那么能表示的范围又将扩展一倍 倘若想要把ID字段再进行扩展 可使用类型long long int 那么至少能有 位 可以创建数不胜数的实例 那么ID为unsigned行吗?如果它的值不会输出到它的父类之外 是可以的 请记住一点 无符号整型与CLS不兼容 (还可选择System::Decimal 其可表示 位 )  例

   using namespace System;using namespace System::IO;public ref class Point  int x; int y; /* */ int ID; /* */ static int nextAvailableID; /* */ static int GetNextAvailableID() return nextAvailableID++;  /* */ static bool traceID = false; /* */ static String^ masterFileLocation; /* */ static Point()   /* a*/ AppDomain^ appDom = AppDomain::CurrentDomain;  /* b*/ masterFileLocation = String::Concat(appDom >BaseDirectory \\\\PointID txt );  /* c*/ try    /* d*/ StreamReader^ inStream = File::OpenText(masterFileLocation);   /* e*/ String^ s = inStream >ReadLine();    /* f*/ nextAvailableID = Int ::Parse(s);   /* g*/ inStream >Close();   /* h*/ appDom >ProcessExit += gcnew    EventHandler(&Point::ProcessExitHandler);     /* i*/ catch (FileNotFoundException^ ioFNFEx)     //采取某些必要的措施    /* j*/ finally     appDom = nullptr;    /* */ static void ProcessExitHandler(Object^ sender EventArgs^ e)   /* a*/ StreamWriter^ outStream = File::CreateText(masterFileLocation);  /* b*/ outStream >WriteLine( nextAvailableID);   /* c*/ outStream >Close();   public: //  /* */ static property bool TraceID   bool get() return traceID;   void set(bool val) traceID = val;   // define instance constructors Point()    /* */ ID = GetNextAvailableID();  X = ;  Y = ;  Point(int xor int yor)   /* */ ID = GetNextAvailableID();  X = xor;  Y = yor;  Point(Point% p) // copy constructor   /* */ ID = GetNextAvailableID();  X = p X;  Y = p Y;  //  /* */ virtual int GetHashCode() override   //   virtual String^ ToString() override   /* */ if (traceID)     return String::Format( [ ]( ) ID X Y);    else     return String::Format( ( ) X Y);   ;

  一旦作为static 标号 至 中定义的成员属于类 而不属于任何实例 而作为private 它们只是一个实现的细节

  使用这个类  C++/CLI在非本地类中 引入了静态构造函数的概念 它的类名声明为static 如上例标号 所示 尽管一个静态构造函数是在类第一次使用之前被调用 但 使用 意味着什么呢?一个引用类静态构造函数的执行 是由类中对某个静态数据成员的第一次引用触发的   根据C++/CLI标准 一个静态构造函数不应有一个ctor初始化过程(ctor initializer) 静态构造函数也不可以被继承 且不能被直接调用 如果一个类的初始化过程带有静态字段 那么这些字段会在静态构造函数执行之前 以声明的顺序被初始化 为静态构造函数生成的元数据总会标记为private 而不管它们是否带有声明或暗指的访问指定符 (但编译器会发出警告 Accessibility on class constructor was ignored ) 在本文写作时 至于一个带有给定访问指定符的静态构造函数 是否应为private之外的问题 仍在讨论之中 因此 访问指定符总是会被忽略   而一个没有显式指明静态构造函数的引用类 它的行为 就会像是有一个空的静态构造函数体一样   在上例标号 a中 利用AppDomain类 为当前线程获取了应用程序域(Application domains) 而根据CLI标准库 应用程序域表现为System::AppDomain对象 提供了隔离性 卸载及托管代码执行时的安全边界检查 多个应用程序域可运行于单个进程中 但是 也不存在应用程序域与线程的一对一关系 可以同时有几个线程属于某一个应用程序域 且同时某一个既定的线程也不会限制在某个单独的应用程序域中 但无论何时 一个线程只能在一个应用程序域中执行   用于追踪在程序执行时下一个可用的ID的文本文件名为 PointID txt 与可执行程序位于同一目录中 如标号 b所示 (Concat可同时用于一个Unicode宽字符串及普通窄字符串 其会在编译时自动转换为宽字符串 )在标号 d中打开此文件 并在标号 e中读取 输入的字符串在标号 f中转换为一个整数 接着 在标号 g中关闭此文件 而try/catch块用于可能抛出的I/O异常   只读属性BaseDirectory与CurrentDomain是Microsoft对标准CLI库的扩展   在I/O中使用的类型 如StreamReader与File 存在于System::IO命名空间中   标号 h注册了一个处理函数 用于在程序快要结束时调用 注意 对一个类来说 没有静态析构函数   Finally子句  C++/CLI支持对try/catch的一个扩展 也就是finally子句 位于它块内的代码总会被执行 而不管对应的try块中是否产生了一个异常 这就是说 finally子句会在try块正常结束后执行 或者说 会在与try相联的catch块之后执行   在上例的标号 j中 finally子句只是简单地把appDom句柄设置为null值 因此 就不会再对AppDomain对象进行访问了 但这种做法有点多余 因为父类块退出时 总会执行到这一行 所以 在此只是作为一个对此功能的简要介绍   事件处理  CLI支持事件的概念 简单来说 一个事件就是一个非本地类成员 它可使一个对象或类提供通知机制 标准CLI类System::AppDomain包含了几个这样的事件 但Microsoft的扩展版本甚至包含了更多的事件 比如说ProcessExit 其在例 的标号 h中被引用   当一个特定的事件发生时 与事件相联的函数会以它们之前相联的顺序被调用 从最简单的形式来说 一个事件只与一个函数发生联系 而这也只是通过简单的赋值完成的 也就是说 包装了函数的代理被赋值给事件成员 而从更一般的形式来说 一个事件在不同时间 通过 += 复合赋值操作符 可与任意多个函数相联 之所以在标号 h中使用这个操作符 是因为不知道事件是否已与事件处理程序相联 如果已经相联 又使用了简单的 = 赋值符 那么这些函数将不再与此事件相联系   每个事件都有一个类型 以ProcessExit来说 类型为System::EventHandler 其是一个用于包装分别接受两个参数System::Object^ 与System::EventArgs^ 函数的代理类型 且有一个void返回类型 而定义在标号 中的ProcessExitHandler函数 也正好具有同样的特征(参数类型) 同时 在标号 h中 把此函数注册为一个事件处理程序 以便在进程退出的事件发生时调用 当这个函数被调用时 它会覆写此前的文本文件 写入一个下次执行时可用的ID值 而传递进来的参数会被忽略   代理  根据C++/CLI标准 代理定义为一个从System::Delegate继承而来的类 它可用于调用所有带有一组参数的代理实例的函数 (注意 与指向成员函数的指针不同 一个代理实例能被绑定至任意类的成员 只要函数类型与代理类型匹配就可 所以 代理非常适合于 匿名 调用 )  而在本例中 用到了一个定义在CLI库中的代理类型 名为System::EventHandler 然而 使用关键字delegate 也能定义自己的代理类型 在标号 h中 就使用了gcnew创建了一个代理的实例 由于被包装的函数为static 而构造函数的调用也只给了一个参数 所以 指向成员函数ProcessExitHandler的指针 其类型也必须与代理相匹配 (要包装一个实例函数 必须提供实例自身的句柄作为第一个参数 )

cha138/Article/program/net/201311/12046

相关参考

知识大全 构造函数和析构函数

  类构造函数  本节将讨论三种类构造函数  类构造函数的类型  注释  实例  用于创建并初始化类的实例  私有  在类之外不可访问的特殊类型实例构造函数无法用私有构造函数来实例化类  静态  在创

知识大全 Java 封闭之构造函数

Java封闭之构造函数  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  &nb

知识大全 asp array 数组函数实例

  asp教程array数组函数实例asp数组函数用于创建一个静态的一维数组你不能声明一个动态数组使用阵列功能  请注意在一个数组的第一个元素总是标记例如零myArray的()  该清单的论据是价值将

知识大全 静态成员

   除了publicprivate和protected以外你还可以把成员说明为static(静态)static修饰符表明一个变量或成员函数对所有类的实例都是相同的你也许想使用一个所有Rectangl

知识大全 .net编程之路(四)

  现在我们来看看这段代码  如果创建这样的一个实例carMyCar=newcar(mazida)  那么这个实例调用的构造函数是初始化器转向执行带两个参数的构造函数  只读字段  readonly字

知识大全 asp 函数与asp过程实例教程

  asp函数与asp过程提供了一种方法来创建重新编程代码可用模块和避免重写的代码每次您执行特定任务同一个块如果你没有任何职能/ASP页中的程序ASP页从上到下执行时ASP解析引擎只是处理从开始到结束

知识大全 php快速url重写实例

  以上的版本才能使用继承了上一个版本的快速重定向的特点(单独类全部使用静态调用)增添了一个很重要的功能和属性可以调用其他url中的模块了也使得模块与模块间或页面与页面间的函数简化共享得以实现  ht

知识大全 C++/CLI基本数据类型探索

C++/CLI基本数据类型探索  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  C++/CLI所支

知识大全 构造和析构成员函数

    类有一个特殊的成员函数叫做构造成员函数它通常用于初始化类的数据成员在创建对象时会自动调用类的构造成员函数Java中的构造成员函数必须与该类具有相同的名字另外构造成员函数一般都应用public类

知识大全 privte的析构函数和构造函数

  C++中是不允许virtual构造函数的如果在构造函数前加virtual  以vc为例编译器报错为inlineistheonlylegalstorageclassforconstructors