|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
我觉得这个学习方法很重要。初学者应该跟我一样有同样一个毛病。那就是急于求成。很想就自己做出个小小的系统来。可真要动手,却又茫然而不知所措。为什么会这样呢?因为我们没有耐心去学习基础知识。写根本看不到什么效果的测试代码。VisualC++2005(VisualC++8.0)中的C++撑持C++/CLI,C++/CLI同时撑持C++言语特征和.NET编程言语特征,能够完成.NETCLI不撑持而C++撑持的言语特征,比方多重承继等,还撑持夹杂Assembly,能够在统一Assembly中夹杂利用CIL代码和本机代码。
C++/CLI的功效要比C#强的多,更合适作为剖析.NETFramework内核的编程言语,笔者试编写了一个C++/CLI程序,试图经由过程反汇编编译天生的.NETAssembly可实行文件,剖析C++/CLI中类的实质。
笔者编写的这个程序是一个实验性的单数和实数盘算程序,上面是C++主程序(TypesSample1_CPP.cpp):
//TypesSample1_CPP.cpp:主项目文件。
#include"stdafx.h"
usingnamespaceSystem;
//Complex类,.NETCTS值范例。
valueclassComplex
{
public:
doubleRe;
doubleIm;
Complex(doubleRe,doubleIm);
};
Complex::Complex(doubleRe,doubleIm)
{
this->Re=Re;
this->Im=Im;
}
//ComplexMath范例,.NETCTS援用范例。
refclassComplexMath
{
public:
ComplexAdd(ComplexOpNum1,ComplexOpNum2);
ComplexMinus(ComplexOpNum1,ComplexOpNum2);
};
ComplexComplexMath::Add(ComplexOpNum1,ComplexOpNum2)
{
ComplexResult1;
Result1.Re=OpNum1.Re+OpNum2.Re;
Result1.Im=OpNum1.Im+OpNum2.Im;
returnResult1;
}
ComplexComplexMath::Minus(ComplexOpNum1,ComplexOpNum2)
{
ComplexResult1;
Result1.Re=OpNum1.Re-OpNum2.Re;
Result1.Im=OpNum1.Im-OpNum2.Im;
returnResult1;
}
//DisplayComplex类,C++类。
classDisplayComplex
{
public:
voidDisplay(ComplexC);
};
voidDisplayComplex::Display(ComplexC)
{
String^ReStr,^ImStr;
ReStr=C.Re.ToString();
ImStr=C.Im.ToString();
Console::WriteLine(ReStr+"+"+ImStr+"i");
}
//RealMath类,C++类,编译本钱机代码。
#pragmaunmanaged
classRealMath
{
public:
doubleAdd(doubleOpNum1,doubleOpNum2);
doubleMinus(doubleOpNum1,doubleOpNum2);
};
doubleRealMath::Add(doubleOpNum1,doubleOpNum2)
{
returnOpNum1+OpNum2;
}
doubleRealMath::Minus(doubleOpNum1,doubleOpNum2)
{
returnOpNum1-OpNum2;
}
#pragmamanaged
//主函数
intmain(array<System::String^>^args)
{
//C++/CLI中,.NETCTS值范例变量间接界说。
ComplexC1(2,2),C2(1,1);
ComplexC3,C4;
//C++/CLI中,.NETCTS援用范例的对象一般利用gcnew关头字创立。
ComplexMath^CM1=gcnewComplexMath();
//C++/CLI中,C++类的对象仍旧利用new关头字创立。
DisplayComplex*DC1=newDisplayComplex();
RealMath*RM1=newRealMath();
C3=CM1->Add(C1,C2);
C4=CM1->Minus(C1,C2);
DC1->Display(C3);
DC1->Display(C4);
Console::WriteLine(RM1->Add(2,1));
Console::WriteLine(RM1->Minus(2,1));
//C++/CLI中,利用new关头字创立的C++类的对象,仍旧必要利用delete关头字删除。
deleteDC1;
deleteRM1;
return0;
}
程序中Complex类利用valueclass关头字界说,属于.NETCTS值范例(ValueTypes);ComplexMath类利用refclass关头字界说,属于.NETCTS援用范例(ReferenceTypes);DisplayComplex和RealMath类则利用class关头字界说,属于C++类,个中RealMath类的界说和完成还利用了#pragmaunmanaged和#pragmamanaged编译唆使,申明RealMath类编译本钱机代码,而不编译成CIL代码,以完成夹杂Assembly。
程序编译运转后,失掉了预期了局:
3+3i
1+1i
3
1
将编译后天生的可实行文件(Assembly)TypesSample1_CPP.exe利用IDAPro4.8反汇编(不要利用ildasm.exe反汇编),注重利用.NETExecutable文件格局反汇编,反省天生的IL汇编言语程序,能够看到以下IL汇编言语代码(往失落了部分正文,另有一部分正文是笔者加的,下同):
……
//Complex类,从System.ValueType类派生,明显是值范例。
.classprivatesequentialsealedansiComplexextends[mscorlib]System.ValueType
{
.fieldpublicfloat64Re
.fieldpublicfloat64Im
.methodpublichidebysigspecialnamevoid.ctor(float64Re,float64Im)
{
ldarg.0
ldarg.1
stfldfloat64Complex::Re
ldarg.0
ldarg.2
stfldfloat64Complex::Im
ret
}
}
//ComplexMath类,从System.Object类派生,明显是援用范例。
.classprivateautoansiComplexMathextends[mscorlib]System.Object
{
.methodpublichidebysigvalueclassComplexAdd(valueclassComplexOpNum1,valueclassComplexOpNum2)
{
.locals(valueclassComplexV0)
ldloca.s0
initobjComplex
ldloca.s0
ldarga.s1
ldfldfloat64Complex::Re
ldarga.s2
ldfldfloat64Complex::Re
add
stfldfloat64Complex::Re
ldloca.s0
ldarga.s1
ldfldfloat64Complex::Im
ldarga.s2
ldfldfloat64Complex::Im
add
stfldfloat64Complex::Im
ldloc.0
ret
}
.methodpublichidebysigvalueclassComplexMinus(valueclassComplexOpNum1,valueclassComplexOpNum2)
{
.locals(valueclassComplexV0)
ldloca.s0
initobjComplex
ldloca.s0
ldarga.s1
ldfldfloat64Complex::Re
ldarga.s2
ldfldfloat64Complex::Re
sub
stfldfloat64Complex::Re
ldloca.s0
ldarga.s1
ldfldfloat64Complex::Im
ldarga.s2
ldfldfloat64Complex::Im
sub
stfldfloat64Complex::Im
ldloc.0
ret
}
.methodpublichidebysigspecialnamevoid.ctor()
{
ldarg.0
callvoid[mscorlib]System.Object::.ctor()
ret
}
}
//DisplayComplex类,C++类,编译后酿成了值范例,并且办法成员消散了。
.classprivatesequentialsealedansiDisplayComplexextends[mscorlib]System.ValueType
{
}
//RealMath类,C++类,编译后酿成了值范例,并且办法成员消散了。
.classprivatesequentialsealedansiRealMathextends[mscorlib]System.ValueType
{
}
……
DisplayComplex类和RealMath类这两个C++类,办法成员为何消散了?进一步反省的IL汇编言语程序,还能够看到以下IL汇编言语代码:
……
.moduleTypesSample1_CPP.exe
……
//DisplayComplex类的Display办法,注重这是一个全局办法(函数),可是仍旧利用CIL代码完成。
.methodassemblystaticvoidDisplayComplex.Display(modopt([mscorlib]System.Runtime.CompilerServices.IsConst)modopt([mscorlib]System.Runtime.CompilerServices.IsConst)valueclassDisplayComplex*C,valueclassComplex)
{
.locals(classSystem.StringV0,
float64V1,
float64V2)
ldarga.s1
ldfldfloat64Complex::Re
stloc.2
ldloca.s2
callclassSystem.String[mscorlib]System.Double::ToString()
ldarga.s1
ldfldfloat64Complex::Im
stloc.1
ldloca.s1
callclassSystem.String[mscorlib]System.Double::ToString()
stloc.0
ldstr"+"
callclassSystem.String[mscorlib]System.String::Concat(classSystem.String,classSystem.String)
ldloc.0
callclassSystem.String[mscorlib]System.String::Concat(classSystem.String,classSystem.String)
ldstr"i"
callclassSystem.String[mscorlib]System.String::Concat(classSystem.String,classSystem.String)
callvoid[mscorlib]System.Console::WriteLine(classSystem.String)
ret
}
//main函数(主函数),注重这是一个全局办法。
.methodassemblystaticint32main(classSystem.String[]args)
{
……
}
……
//RealMath类的Add办法,注重这是一个全局办法,利用PInvoke(平台挪用)挪用本机代码完成。
.methodpublicstaticpinvokeimpl(/*Nomap*/)modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall)float64RealMath.Add(modopt([mscorlib]System.Runtime.CompilerServices.IsConst)modopt([mscorlib]System.Runtime.CompilerServices.IsConst)valueclassRealMath*,float64,float64)nativeunmanaged
{
}
//RealMath类的Minus办法,注重这是一个全局办法,利用PInvoke(平台挪用)挪用本机代码完成。
.methodpublicstaticpinvokeimpl(/*Nomap*/)modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall)float64RealMath.Minus(modopt([mscorlib]System.Runtime.CompilerServices.IsConst)modopt([mscorlib]System.Runtime.CompilerServices.IsConst)valueclassRealMath*,float64,float64)nativeunmanaged
{
}
……
读者读到这里大概会感应有些奇异:不是说.NET编程言语(比方C#)源程序完整由类组成吗?那末编译后天生的可实行文件反汇编后,就不该该另有全局办法(函数),为何反汇编了局中居然呈现了全局办法呢?
实践上,.NETCLI的第2部分“元数据的界说和语义”中已明白申明:字段和办法都能够是全局的,也就是不在任何一个范例中界说,而在模块(Module)中间接界说的字段和办法。
固然C#等.NET编程言语纷歧定撑持全局字段和全局办法,可是C++/CLI是撑持全局字段和全局办法的,C++/CLI中的C++全局变量和全局函数,就是利用全局字段和全局办法完成的。
实在,说.NET编程言语源程序中完整没有全局字段和全局办法,这类说法自己就是不太公道的,范例中的静态成员,包含静态字段和静态办法,实质上不也就相称于加了范例名“名字空间”——相称于“前缀”的全局字段和全局办法吗?
C++言语特征分明分歧于C#,C++/CLI仍旧撑持C++言语特征。比方:在C#中,利用struct关头字界说的布局属于.NETCTS值范例,利用class关头字界说的类属于.NETCTS援用范例,可是在C++/CLI中,struct关头字和class关头字是等价的,区分只是利用struct关头字界说的类中,成员默许是public的,而利用class关头字界说的类中,成员默许是private的。
回到上述反汇编了局,从反汇编了局中,能够分明看出以下几点:
1、C++/CLI中间接利用class大概struct关头字界说类,并非间接界说.NETCTS中的范例。C++/CLI中界说.NETCTS中的范例,自界说值范例必需利用valueclass大概valuestruct关头字界说,自界说援用范例必需利用refclass大概refstruct关头字界说。
2、关于C++/CLI中的C++类,也就是间接利用class大概struct关头字界说的类,实在现办法,包含类的对象的完成办法,仍旧与传统C++完成类和对象的办法类似:对象的属性(字段大概成员变量)和办法(成员函数)分别,对象计划成包括对象属性的数据布局,办法计划成针对对象属性的历程(函数),将包括对象属性的数据布局的援用(指针)作为办法历程的参数,让办法历程针对特定对象的属性举行操纵,如许包括对象属性的数据布局和办法历程在逻辑上就组成一般意义上的对象。以是C++类编译后,实践上对应的.NETCTS范例中只包括字段成员,办法成员酿成了全局办法。由于传统C++中,类都相称于值范例,C++对象变量间接存储C++对象自己,以是C++/CLI中的C++类编译后对应的.NETCTS范例也都是值范例。
3、关于夹杂Assembly中编译本钱机代码的类,实践上只要办法的完成编译成了本机代码,经由过程平台挪用(PInvoke)举行挪用,好像C++/CLI大概C#挪用本机代码DLL一样。
能够看出,C++/CLI中,除非一个类被明白界说成.NETCTS中的范例,C++类和对象的完成在实质上仍旧和传统C++完成类和对象的办法类似,这就是为何C++/CLI能够完成.NETCLI不撑持而C++撑持的言语特征,比方多重承继等的实质缘故原由。C++/CLI的实质能够借用一句话来讲明:
“.NET的回.NET,C++的还回C++!”
关于C++中类和对象的完成办法,还能够拜见《深度探究C++对象模子》一书(《InsideTheC++ObjectModel》一书的中译本)。
来自:http://blog.csdn.net/Changjiang/archive/2006/11/27/1415972.aspx
可怜的程序员,还是逃不出移植的命运! |
|