CLI 指的是通用语言结构,一种支持动态组件编程模型的多重结构,在许多情况下,这代表了一个与C++对象模型完全颠倒了的模式,一个时实的软件层,有效的执行了系统,在底层操作系统与程序之间运行,操作底层的设备受到一定限制,操作执行程序中的活动类型及与程序相关联的下部结构得到了支持,反斜杠(/) 代表C++和CLI的捆绑。
C++/CLI : 它是静态C++对象模型到CLI的动态组件对象编程模型的捆绑,简而言之,它就是你如何用C++在.net中编程。
CLR实时通用语言是CLI的微软版本,它非常适用于Windows操作系统,相似地,Visual C++2005是C++/CLI的实现。
作为第二个近似的答案,我认为C++/CLI是.NET编程模式与C++的结合,正如以前将模板与C++结合起来产生的泛型编程。所有这种结合中,企业所拥有的C++的投资以及开发人员使用C++的经验将得到保存,而这恰恰是使用C++/CLI进行开发的重要基础。
DOCUMENT
CLI对象模型
1
.追踪句柄
tracking handle
2
.内部指针
interior pointer
3
.固定指针
pinning pointer
追踪句柄
---
指向“托管堆上一个完整的对象(
whole object
)”的指针。是会自动更新的指针
内部指针
---
指向托管堆上“对象所包涵的内部成员”
的指针
固定指针
---
固定托管堆上“对象所包含的数据成员的指针”,使之不能被垃圾收集器控制
2
.数据成员
(
1
)实例字段
:占据存储位置,表示组件
/
对象状态,不同对象的实例字段拥有不同的存储位置
(
2
)静态字段
:占据存储位置,表示组件
/
对象的全局状态,所有对象的静态字段“共享”同一个存储位置
(
3
)常量字段
:编译时常量,必须在声明的同时初始化(内联初始化),默认静态
(
4
)只读字段
:运行时只读,不可以改变,可以在声明的同时进行初始化(只对静态适用),或者在构造器中初始化,并不默认静态
3
.成员函数
(
1
)实例方法,属于对象实例,可以访问实例字段,也可以访问静态字段。
(
2
)静态方法,属于类型,不可访问实例字段,只能访问静态字段。
4
.组件成员
:
成员函数
+
数据成员的变体
(
1
)属性:函数变体,实际上是在背后生成了一个私有字段和两个公有的函数
(
2
)属性(扩展)
private
: int Int_Data;
public
:
property
int x
{
int get()
{
return Int_Data;
}
void set(int value)
{
Int_Data = value;
}
}
(
3
)事件
:
(
1
)委托
– CLI
托管函数指针类型
(
2
)定义一个事件,实际上是在背后定义了一个委托类型的私有字段和三个函数:
(
1
)
add_Click
(
2
)
remove_Click
(
3
)
raise_Click
(
4
)事件扩展:
public ref class Button
{
EventHandler^ handler;
public: event EventHandler^ Click
{
void add(EventHandler^ e)
{
Lock<Mutex> I(m);
handler += e;
}
void remove(EventHandler^ e)
{
Lock<Mutex> I(m);
handler -= e;
}
}
};
托管编程与资源管理
:
资源是软件的主题,使用资源总是难免的,但资源泄露总是可以避免的,
C++/CLI
两大类资源,托管内存和非托管资源,托管内存是托管堆上的内存空间,非托管资源是本地堆内存,文件句柄,数据库连接,网络链接,
GDI
设备句柄
……
垃圾收集器只能追踪托管对象的内存使用,而无法知道费托管资源何时不再被使用,内存释放并不会导致非托管资源被释放。如何在内存被释放之前释放非托管资源
Object.Finalize
方法。
C++/CLI
不允许显式重写
ref
class Destruction
{
protected
: !Destruction()
{
//
实际是在重写
System::Object.Finalize();
}
};
Finalize
会将对象的代龄延长,使无效的对象长时间存在于托管堆中,给托管堆带来内存负担
Finalize
只会为托管资源清理提供了最后一层保障,并不推荐。
System::GC::SuppressFinalize();
告知垃圾收集器不需要再调用
Finalize
,
从而减轻对象的负担
元数据与动态编程
C++/CLI
元数据系统
(1)
动态编程
1 ----
反射
Reflection
(2)
动态编程
2 ----
特性
Attributes
动态程序是指能够在运行时改变自身结构和行为的程序
静态
----
编译时,早约束,紧耦合
动态
----
运行时,迟约束,松耦合
ISO-C++
是一门静态编程语言,只具有非常有限的动态能力,动态堆内存
----
虚函数的动态绑定,有限的类型识别能力
----RTTI
,获得了极高的效率,却丧失了动态编程所具有的高灵活性
--DLL
动态链接库
--COM
组件
反射
Reflection
动态的发现类型,操作类型,创建类型(查询元数据)
特性
Attributes
允许程序定义新的数据类型,从而在运行时动态的感知环境(创建元数据)
元数据(
Metadata
) : 是数据的数据,是
CLI
组件合同的描述载体,组件的平台的粘合剂,
CLI
元数据分为:定义型元数据
–
描述代码中定义了什么,引用型元数据
–
描述代码中引
用了什么,特性
–
扩展定义新的元数据。
泛型编程:
通过参数化类型来实现在同一份代码上操作多种数据类型,泛型编程是一种编程模式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。它意味着一种新的抽象性质,从本质上来讲,它是数据类型的一组需求条件。泛型编程不是容器编程
C++/CLI
支持两大泛型机制,三种泛型应用
(
1
)编译时机制
---- ISO-C++
模版
----
编译时实例化
----
基于签名的隐式约束
(
2
)运行时泛型
---- CLI
泛型
----
运行时实例化
----
基于“基类
+
接口”的显式约束。
应用:
(1)
在
ISO-C++
本地类型上应用模板(编译时泛型)
(2)
在
CLI
托管类型上应用模板(编译时泛型)
(3)
在
CLI
托管类型上应用
CLI
泛型。
(4)
C++/CLI
所支持的泛型程序库
(1)
标准模板库
STL
(2)
CLI
标准模板库
STL.NET
(3)
CLI
泛型库
System::Collections::Generic
eg:
generic <typename T>
public ref class Stack
{
public: T tem;
T Pop()
{
return tem;
}
String^ GetStr()
{
//
用追踪句柄来调用函数成员
return tem->ToString();
}
void Push(T t)
{}
int Size()
{
return 0;
};
};
public: void TestEvery()
{
Stack<System::String^>^ sta = gcnew
Stack<System::String^>();
}
CLI
泛型机制:
第一轮编译时,编译器只为
Stack<T>
类型产生泛型版的
IL
代码与元数据
----
并进行泛型类型的实例化,
T
在中间只充当占位符。
JIT
编译时,当
JIT
编译器第一次遇到
Stack<System::String^>
时,将
String^
替换“泛型版”
IL
代码与元数据中的
T----
进行泛型类型的实例化。
CLI
为所有类型参数为
ref class
的泛型类型产生同一份代码,但是如果参数类型为
value class
,针对每一个不同的
value class
,
CLI
将产生一份独立的代码。
CLI
泛型机制特点
:
(1)
由于
CLI
泛型的实例化会推迟到
JIT
编译阶段进行,因此未经实例化的泛型类型会存在于编译生成的程序集中,成为一个类型实体。
(2)
如果实例化泛型类型的参数相同,那么
JIT
编译器会重复使用该类型,因此
CLI
泛型可以避免
C++
模板可能导致的代码膨胀问题。
(3)
CLI
泛型类型携带有丰富的元数据,因此可以通过反射来获取程序集中的泛型类型的相关信息。
CLI
泛型适用于四种对象:
(1)
CLI
托管类型
(包括引用类型和值类型)
(2)
CLI
接口类型
(3)
CLI
委托类型
(4)
函数
(成员函数,全局函数)
CLI
不适用于:
(1)
ISO-C++
本地类型
----
本地类型没有
CLI
泛型的条件
:元数据的信息
(2)
CLI
属性,事件,索引器,操作符
A.
CLI
泛型接口
:
generic <typename Item>
public interface class IList
{
void ListCount();
};
generic <typename Item>
ref class MyList : public IList<Item>
{
public: virtual void ListCount()
{
Console::WriteLine("Call interface method !");
}
};
B.
CLI
泛型委托
:
//delegate
generic <typename type>
public delegate void MyDelegate(type ty);
public ref struct MyDeleClass
{
public ref class DeleClass
{
public: static void GetInt(int i)
{
Console::WriteLine("i is ");
}
public: void UserDele()
{
MyDelegate<int>^ dele = gcnew MyDelegate<int>(&DeleClass::GetInt);
dele(200);
}
};
C.
CLI
泛型函数
:
ref
class Megedata
{
generic
<typename T>
void ControType(T t)
{
}
};
调用泛型函数:
Megedata^ mana = gcnew Megedata();
mana->ControType<int>(2);
泛型约束
(1)
类型参数约束:
A
.
CLI
泛型采用“基类
+
接口”的方式来实现对类型参数的“显式约束”。
B
.
CLI
泛型不支持
ISO-C++
模板所使用的基于“签名”的“隐式约束”。
C
.虽然获得了编译时类型安全和相关代码清晰,但却丧失了很多灵活性(无法使用静态函数,操
作符、字符、内嵌类型等)
(
2
) 结束代码示例
generic
<typename T>
ref
class MyCollect
{
T t;
public
void Add()
{};
};
T
实现
IComparable
接口
CLI
泛型与
C++
模板:
两个根本性的区别:
(1)
C++
模板为编译时实例化,
CLI
泛型为运行时实例化
(2)
C++
模板为基于“签名”的隐式约束,
CLI
泛型为基于“基类
+
接口”的显式约束。
CLI
泛型不支持如下
C++
模板中的机制:
(1)
非参数类型
(2)
缺省参数值
(3)
模板的模板参数
(4)
模板特化及部分特化。
A
.
非参数类型
template
<class type,int size>
public
ref class StackInfor
{
array
<type>^ m_stack;
int
top;
public
: StackInfor() : top(0)
{
m_stack = gcnew array<type>(size);
}
};
//
调用
StackInfor
StackInfor<int,100>^ stack = gcnew StackInfor<int,100>();
B
.缺省参数值
template
<class type,int size
= 256
>
public
ref class StackInfor
{
array
<type>^ m_stack;
int
top;
public
: StackInfor() : top(0)
{
m_stack = gcnew array<type>(size);
}
};
//
调用
StackInfor
StackInfor<int>^ stack = gcnew StackInfor<int>();
StackInfor<int
,
100
>^ stack = gcnew StackInfor<int
,
100
>();
C
.模板的模板参数
template
<template<class T>class TT,class t>
ref
class MyClass
{
TT<t> m_data;
};
template
<class t>
public
ref class Apple
{};
ref
class Use
{
public
: void Test()
{
MyClass<Apple,int> ^m_clas = gcnew MyClass<Apple,int>();
}
};
D
.模板的局部特化
//-----------
template
<long B,long E>
ref
struct Power
{
literal
long value = B * Power<B,E-1>::value;
};
template
<long B>
ref
struct Power<B,0>
{
literal
long value = 1;
};
//
调用
Console::Write(Power<2,10>::value);