《C++/CLI面向对象编程》(郑阿奇)读书笔记

1 C++/CLI标准

ECMA最早于2003年开始设计,最新版ECMA-372 200511月推出。

2 双关键字

enum calss 

enum struct

for each

interface calss

interface struct

ref class

ref struct

value calss

value struct

3 Object对象

所有对象的根对象,可以调用ToStringGetType等方法。

Int n = 3;

Console::WriteLine(n.GetType());//输出:System.Int32

4特殊类型

System::Decimal,取值范围:±7.9×10(-28)~ ±7.9×10(28),具有28个有效位,适合用于要求有大量有效的整数及小数位数且没有舍入错误的财务计算。

Decimal dcm = System::Convert::ToDecimal(“123456789012345678901.23456789”);

public:Decimal( //构造函数

int lo, //96位整数低32

int mid, //96位整数中32

int hi, //96位整数高32

bool isnegative, //是否为负,false为正

unsigned char scale //比例因子,0~28

)

Decimal d(0xeb1f0ad2, 0xab54a98c, 0, false, 0); //=12345678901234567890

5 for each循环

array<int>^ numbers=gcnew array<int>(1,2,3,4,5);

for each(int I in numbers)

{

Console::WriteLine(i);

6跟踪句柄^和跟踪引用%

int n = 10;

int %rn= n;

String^ str = “hello”;

String^ %rstr = str;

String^ nullStr = nullptr;

跟踪引用与指针很相似,表现在:

1) 通过函数参数传递跟踪句柄如果指针不会产生对象拷贝,函数内部对对象做的任何更改都会影响到外部对象。

2) 跟踪句柄之间的赋值也不会产生对象拷贝,都指向托管堆上的同一个对象。

不同于指针:

1) 不能进行指针加减运算

2) 不能直接传递到非托管函数内部

注意:跟踪句柄作为函数参数时,要得到在函数内部的改变,必须用跟踪引用

void fun(String^ %rstr)

{

rstr = “abc”;

7内部指针和固定指针

使用内部指针interior_prt可以进行地址运算,gc更新地址时会同时更新内部指针

array<int>^ arr = gcnew array<int>{1,2,3,4,5};

interior_ptr<intp = &arr[0];

int n = *(p+3);

使用固定指针pin_ptr可以得到一个稳定的地址,gc不会改变该地址,可转换为普通指针

inti = gcnew int(5);

pin_ptr<intp = &*i;

int *pi = p;

8数组

array<int>^  an= gcnew array<int>(20); //20int元素

array<int>^  an= gcnew array<int>{1,2,3,4,5,6}; //定义并初始化

array<String^>^ astr = gcnew array<String^>{“aaa”,”bbb”}; //跟踪句柄数组

array<int, 2>^ ann = gcnew array<int,2>(3,4); //二维,34

array<array<String^>^>^ aastr = gcnew array<array<String^>^>{ //数组的数组

gcnew array<String^>{“aaa”,”bbb”},

gcnew array<String^>{“ccc”,”ddd”,”eee”},

};

for each(array<String^>^ astr in aastr)

{

for each(String^ str in astr)

{

Console::WriteLine(str);

}

}

数组常用操作:ClearReverseSortResizeGetLength、FindBinarySearch(二分查找)等,调用:Array::Sort(array<T>^ ar),从大到小排序

9字符串String

常用操作:

Clone

Compare

Concat

Contains

Copy

CopyTo

EndsWith

Equals

Format

IndexOf

IndexOfAny

Insert

IsNullOrEmpty

Join

LastIndexOf

LastIndexOfAny

PadLeft

PadRight

Remove

Replace

Split

StartsWith

Substring

ToCharArray

ToLower

ToUpper

Trim

10控制台输入输出

ReadLine //读一行,回车结束

Read //读一字符,回车结束

ReadKey //返回ConsoleKeyInfo,不会因为回车结束

WriteLine

Write

11 可变长参数的函数

void Say(...array<String^>^ astr)

{

for each(Stringstr in astr)

{

Console::WriteLine(str);

}

}

Say("aaa","bbb","ccc","ddd","eee","ffff");

12 CLI结构和类

值结构类和类类:value structvalue class

引用结构类和引用类类:ref structref class

与本地C++类不同:

1) 不能包含本地C++数组和C++类对象的数据成员

2) 不能包含友元函数

3) 不能包含类似于C语法中位字段的数据成员

4) 成员函数不能声明成const,而使用关键字literal

5)对于引用类,当有其他构造函数时,必须重新显式定义无参的默认构造函数

13 属性

关键字:propertysetget

类型:

1) 标量属性,单个属性值,根据需要定义setget,不定义则为简单属性

2) 静态属性property static,对应静态数据成员

3) 索引属性,对应一组值,可通过[]访问,默认索引属性default和名称索引属性

ref class Numbers

{

public:

Numbers()

{

defaultArray = gcnew array<String^>{“aaa”,”bbb”,”ccc”}

}

property String^ default [int]

{

if(index<0) index=0;

else if(index > defaultArray->Length - 1)

index = defaultArray->Length-1;

return defaultArray[index];

}

private:

array<String^> defaultArray;

}

使用:

Numbers num;

Console::WriteLine(num[1]);

14 literalinitonly字段

C++/CLI类中,数据成员不能直接初始化,即使使用const修饰。可以使用literalinitonly

literal为字面字段,定义时必须初始化,不能被修改,是编译时常量且只能用于值类、引用类和接口类;

initonly为只构字段,仅在构造函数进行初始化,可赋值多次,静态只构字段只能在静态构造函数中初始化。

15 拷贝和静态构造

拷贝构造函数,左起第一个参数必须是引用类的引用对象或跟踪句柄

静态构造函数,用来初始化类的静态成员,不能带参数,必须私有,被系统自动调用。

16 析构和终结器

终结器finalizer,格式!ClassName(),当引用类对象被销毁时,由垃圾回收器自动调用。

只有当对引用类对象调用了delete时,析构函数才被调用,此时终结器将不再被调用,但对象本身的内存资源还是由垃圾回收器最终回收;

终结器中应当释放非托管资源;

析构函数中应当释放托管资源和非托管资源,其中释放非托管资源的部分功能可以调用终结器,避免重复代码。

参考:

http://msdn.microsoft.com/zh-cn/library/ms177197.aspx

http://technet.microsoft.com/zh-cn/windows/0s71x931(v=vs.110)

http://dev.yesky.com/msdn/450/2420450_2.shtml

17 继承和多态

可以使用new禁止虚函数的多态,virtual void Show() new{}

允许将基类的虚函数直接赋值给派生类中的虚函数,派生类的虚函数就拥有多个函数名,且还可以通过赋值来改变重载的次序。

ref class Animal 

{

public:

virtual void Speak()

{

//动物叫

}

}

ref class Dog:public Animal

{

public:

virtual void Speak() override //标准重载

{

//大狗叫

}

}

ref class Puppy:public Dog

{

public:

virtual void Yip() = Dog::Speak //重置Dog的虚函数

{

//小狗叫

}

}

ref class Cat:public Animal

{

public:

virtual void Speak() new //重新指定,不具AnimalSpeak虚函数多态

{

//小猫叫

}

}

ref class Tiger:public Cat

{

public:

//尽管该类从Cat派生,但重置虚函数Grow依次对AnimalCat类的虚函数Speak进行重载

virtual void Growl()=Animal::Speak, Cat::Speak

{

//老虎叫

}

}

array<Animal^>^ animals = gcnew array<Animal^>

{

gcnew Animal(),

gcnew Dog();

gcnew Puppy();

gcnew Cat();

gcnew Tiger();

}

for each(Animal ^a in animals)

{

a->Speak();

}

Animal^ cat1 = gcnew Cat();

Cat^ cat2 = gcnew Cat();

Cat^ tiger = gcnew Tiger();

cat1->Speak();

cat2->Speak();

tiger->Speak();

输出:

动物叫

大狗叫

小狗叫

动物叫

老虎叫

动物叫

小猫叫

老虎叫

18 抽象和密封

显示声明抽象类,需要关键字abstract

ref class B abstract{};

或者类中包含纯虚函数

virtual void Show() abstract;

密封类不能被派生,关键字sealed

ref class B sealed{};

也可以声明一个密封成员函数,该函数不能被派生类重写

virtual void Show() sealed;

19 接口、委托和事件

引用类,只需单继承,但可以继承多个接口,接口比类的内涵广,可以包含成员函数、属性、索引器、静态方法、静态字段、静态构造以及事件等,但接口方法不能包含任何实现。

关键字:interface class

不指定成员访问属性时,默认是公有public

约定类名前缀:接口I,值类V,简单类S,引用类R

派生类不能改变接口函数、属性等的公有属性,且必须在函数或属性前加上virtual关键字

委托是封装了函数指针的一种对象,CLI中不允许向函数传递函数指针,但可以传递委托对象。

两种委托类型:单函数委托Systme::Delegate和多播委托System::MulticastDelegate

委托步骤:创建委托->创建被委托的全局函数或全局成员->将函数放入委托中->向多播链中添加或移除委托->调用委托

创建一个委托:public delegate void MyFunDelegate(String^ str);

用于委托的方法必须是一个全局函数、一个静态方法或是一个公有的成员函数。

void MyFun(Stringstr)

{

Console::WriteLine("MyFun:"+str);

}

ref class RClass

{

public:

static  void StaticFun(Stringstr)

{

Console::WriteLine("StaticFun:"+str);

};

void NormalFun(Stringstr)

{

Console::WriteLine("NormalFun:"+str);

};

};

MyFunDelegate ^rmf = gcnew MyFunDelegate(&MyFun);

MyFunDelegate ^rmf2 = gcnew MyFunDelegate(&RClass:: StaticFun);

RClass ^rc = gcnew RClass();

MyFunDelegate ^rmf3 = gcnew MyFunDelegate(rc, &RClass:: NormalFun);

增加或去除一个委托

rmf += gcnew MyFunDelegate(&RClass:: StaticFun);

rmf -= gcnew MyFunDelegate(&RClass:: StaticFun);

委托调用

rmf->Invoke(“hello”);

rmf(“hello”);

定义事件两个步骤:

1定义委托

2通过event关键字定义该委托引用类型的委托对象来声明事件

public delegate void DoorHandler(String^ name);

event DoorHandler^ Knock;

event DoorHandler^ Leave;

Door^ door = gcnew Door;

Knock += gcnew DoorHandler(door, &Door::Someone_Knock);

Leave += gcnew DoorHandler(door, &Door::Someone_Leave);

Knock(“someone”);会引发Knock事件,委托中的处理函数被执行

【疑问】:能调用Knock的地方为什么不能调用委托,或者干脆直接调用最终执行函数,如果说委托是为封装函数指针而用,那么事件真的想不通可用之处,事件可以添加多个委托,那么委托照样可以添加多个执行函数!

【领悟】困扰许久,终于发现一点事件和委托的差异,就是关于“封装设计”:

如果要很好的将委托属性封装到类内部,必须提供一个可向委托添加和删除执行函数的接口,可是要传递函数指针本身就需要依赖委托,所以接口中只能传递委托,这样一来,每次赋值都会完全覆盖之前的对象,这个内部委托对象便不能够很好地进行维护。所以当上升到事件层面之后,这个问题便迎刃而解。并且事件,本身已经具备了很好的可控的操作委托的接口,定义为类属性的事件只能被本类触发。就这一点看来,

执行函数封装--->委托---封装-->事件

他们位于不同层面的概念,根据自己的设计需要来使用。)

20 finally

如果某些操作希望必须被执行,即使发生了异常,那么就用finally

try{

}

catch()

{

}

finally

{

//执行完try块后不管是否跳进catch块,这里都会执行

}

当抛出一个不会被捕获的异常时,程序将崩溃,托管资源将不能优雅地释放。

只有当异常被捕获时,程序正常退出,托管资源才可以被正常释放。

也可以是以下组合

//申请资源

try 

{

//使用资源

}

finally

{

//delete手动释放资源

}

21 文件和流

I/O

File

FileInfo

DriveInfo

FileStream

Path

Directory

DirectoryInfo

DeflateStream //适用于流式的数据

GZipStream //适用于文件数据

SerialPort

流的读写类

BinaryReaderBinaryWriter:二进制数据流读取和写入

StreamReaderStreamWriter:带有特定编码格式的文本文件的读取和写入

StringReaderStringWriter:字符串中字符的读取和写入

通用I/O

BufferStream

CryptoStream

MemoryStream

NetworkStream

目录操作

Directory::Exists(“c:\\aaa”); //判断目录存在

Directory::CreateDirectory(“c:\\aa”) //创建目录

Directory::Move //移动目录

Directory::Delete //删除目录

Directory::GetDirectories //获取子目录列表

Directory::GetFiles //获取目录中文件列表

文件操作

File::Exists

File::Create

File::CreateText

File::Open

File::OpenText

File::OpenRead

File::OpenWrite

File::Delete

File::Move

File::Copy

File::

File::

特性限定

在结构体、类或方法前可以用中括号进行某些特性限定或说明,例如MarshalAs属性用来指示如何在托管代码和非托管代码之间封送数据。如托管代码中调用WindowsAPI时需要使用。

[StructLayout(LayoutKind::Sequential)]

public value struc SS

{

//指明szDisplayName成员映射到非托管类型ByValTStr(定长字符数组)

[MarshalAs(UnmanagedType::ByValTStr, SizeConst=260)]

String^ szDisplayName;

}

限定结构体顺序布局,还有一种精确布局

[StructLayout(LayoutKind::Explicit)]

struct SS //相当于联合体union结构

{

[FieldOffset(0)]

int a;

[FieldOffset(0)]

int b;

}

[DllImport(“shell32.dll”)] 声明函数来自的动态库

文件流操作

FileStream支持同步、异步读写

FileStream::Read

FileStream::Write

FileStream::BeginRead //异步

FileStream::BeginWrite //异步

FileStream::EndRead //异步

FileStream::EndWrite //异步

FileStream::Seek //随机访问

FileStream::ReadByte

FileStream::WriteByte

FileInfo^ fi = gcnew FileInfo(“C:\\aa.txt”);

FileStream^ fs = fi->OpenRead();

int nCount = 100;

array<Byte>^ BArray = gcnew array<Byte>(nCount);

int nRead = fs->Read(BArray, 0, nCount);

if(fs->Length == fs->Position)  //判断达到文件尾

fs->Close();

FileStream^ fo = File::Open(“C:\\aa.txt”, FileMode::Append);

array<Byte>^ info = (gcnew UTF8Encoding(true))->GetBytes(value);

fo->Write(info, 0, info->Length);

fo->Close();

文本流

StreamReader^ sreader = gcnew StreamReader(“C:\\aa.txt”, System::Text::Encoding::Default);

或 StreamReader^ sreader = gcnew StreamReader(File::OpenRead(“C:\\aa.txt”), 

System::Text::Encoding::Default);

String^ strLine;

while(sreader->Peek()>=0)

{

strLine = sreader->ReadLine();

}

sreader->Close();

StreamWriter^ swriter = gcnew StreamWriter(“C:\\aa.txt”);

String^ logFile = DataTime::Now.ToShortDateString()->Replace(“/”,S”-”)->Replace(“\\”,S”-”);

logFile->Concat(“.log”);

FileStream^ fs = File::Open(logFile, FileMode::CreateNew, FileAccess::Write, FileShare::None);

StreamWriter^ sw = gcnew StreamWriter(fs);

sw->Flush();

sw->Close();

字节流和串行化

BinaryReader^ bs = gcnew BinaryReader(File::Open(logFile, FileMode::Open));

BinaryWriter^ bw = gcnew BinaryWriter(File::Open(logFile, FileMode::Create));

[Serializable]

ref class TestSimpleObject

{

private:

int  m1;

String^ str1;

public:

[NonSerialized]

String^ str2;

Test()

{

m1 = 1;

str1= “aaa”;

str2 = “bbb”;

}

}

若从TestSimpleObject派生类,那么派生类也必须使用Serializable属性进行标记。

TestSimpleObject^ obj = gcnew TestSimpleObject;

FileStream^ stream = File::Open(“aa.dat”, FileMode::Create);

BinaryFormatter* formatter = new BinaryFormatter();

formatter->Serialize(stream, obj);

stream->Close();

“aaa”

obj = nullptr;

stream = File::Open("aa.dat", FileMode::Open);

formatter = new BinaryFormatter();

obj = dynamic_cast<TestSimpleObject^>(formatter->Deserialize(stream));

stream->Close();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值