转自:http://blog.csdn.net/accesine960/article/details/135803
C++/CLI简介如下:
托管的C++(MC++)饱受诟病的一个地方就是语法格式和普通的编程语言差别很大,很多人都评价为: ugly 和 twisted 语法。
因为.net 的基础之一Common Language Infrastructure CLI是提交给了ECMA标准委员会。为了改进现有的MC++ ECMA在 2003年10月份起动了一个新的工作小组,其任务是为ISO标准的C++和CLI之间的交互操作提供一组新的语言扩展。这也就是C++/CLI的由来。
改进前的MC++有如下缺点:
1、 语法格式不雅观,比如很多双下划线的关键字;
2、 托管的C++在和CLI交互是并不能使用全部的功能,比如在C#或者VB.NET中可以使用 for-each 等语法,而MC++不可以;
3、 MC++提供不了一些标准C++的语言特性,比如:C++中的模板,和CLI的内存收集(garbage collection);
4、 非托管的指针和托管的引用指针在语法上都用*表示,容易混淆;
5、 MC++的编译器的效果不好。
现在的C++/CLI也就是Whidbey中的MC++,完全解决了上面的ugly 和 twist的问题。
下面我们来介绍一些特性:
还是从HelloWord 开始吧:
void _tmain()
{
//^ 号是新引进的语法,表示一个handler
String^ str = "Hello World";
Console::WriteLine(str);
}
上免得 ^str handler就表示指向一个托管对象的引用。
Handler 和 C++中的指针的区别如下:
1、 语法上 用 ^和*区别;
2、 Handler是建立在托管堆上的一个被(CLI)管理的引用,而指针指向一个物理的内存地址;
3、 指针不受垃圾回收器的影响,而handler实际指向的内存地址则收垃圾回收器的管理;
4、 使用指针必须用声明使用delete来释放内存,而handler可以使用也不可以使用;
5、 Handler是类型安全的,每个指向托管对象的handler都对应一个具体的类型,而指针则没有要求,比如可以使用void * 指针;
6、 语法上使用new返回一个指针,而gcnew返回一个handler。
请看下面的例子:
void _tmain()
{
String^ str = gcnew String("Hello World");
Object^ o1 = gcnew Object();
Console::WriteLine(str);
}
上例gcnew用来在CLR堆上初始化一个托管对象并返回一个handler。这样的语法可以和指针的声明很清楚的区别开来。
看一个具体的例子:
using namespace System;
interface class IDog
{
void Bark();(看一个网友评论说是void virtual Bark();)
};
ref class Dog : IDog
{
public:
void Bark()
{
Console::WriteLine("汪汪");
}
};
void _tmain()
{
Dog^ d = gcnew Dog();
d->Bark();
}
转自:http://blog.csdn.net/luqinghua/article/details/1551418
深入学习C++/CLI
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);
0 0