=================================版权声明=================================
版权声明:本文为博主原创文章 未经许可不得转载
请通过右侧公告中的“联系邮箱(wlsandwho@foxmail.com)”联系我
未经作者授权勿用于学术性引用。
未经作者授权勿用于商业出版、商业印刷、商业引用以及其他商业用途。
本文不定期修正完善,为保证内容正确,建议移步原文处阅读。 <--------总有一天我要自己做一个模板干掉这只土豆
本文链接:http://www.cnblogs.com/wlsandwho/p/4713311.html
耻辱墙:http://www.cnblogs.com/wlsandwho/p/4206472.html
=======================================================================
第九章 值类型
值类型
C++等价形式是.net类型名称的别名,用哪个都一样。
所有值类型都是从System::ValueType继承。
栈上存储
不被垃圾回收
直接访问没差别
值类型有必要时可以当作对象使用
结构
结构是值类型而非引用类型。(例如表示坐标的点)
.net结构的优点就是可以由其他语言来使用。
不支持继承也不能作为基类
能实现接口
复制结构,是拷贝。
结构成员不可以是引用类型,引用成员必须要垃圾回收。
可嵌套定义。
value struct Point
{
int x;
int y;
};
value struct Person
{
String^ name;
value struct Date { int yyyy,mm,dd;};
Date dob;
};
枚举
值类型 派生自System::Enum,System::ValueType。
栈上
枚举成员要有类型名称限定
Format函数 String^ s=Enum::Format(WeekDay::typeid,w,"G");
一定要有public或private,因为C++11标准产生C2644错误。
枚举支持任何整数类型,节省内存
public enum class Weekday:char
=======================================================================
第十章 操作符重载
操作符重载
C++/CLI的操作符重载限制比较大,主要是因为在.net中要兼容各语言。
遵循CLS
gcnew和delete不恩那个重载因为内存是由.net的运行时负责的。
Equals函数
virtual bool Equals(Object^ other) override
{
IntVal^ obj = dynamic_cast<IntVal^>(other);
if (obj==nullptr)
{
return false;
}
return value == obj->value;
}
重写Equals时也应该重写GetHashCode
标准C++重写++时是前++和后++
C++/CLI重写++时只有++
转换操作符+精彩操作符
static operator IntVal(int v)
{
return IntVal(v);
}
static IntVal operator+(IntVal liv,IntVal riv)
{
IntVal result(liv.value+riv.value);
return result;
}
value struct IntVal
{
private:
int value;
public:
IntVal(int v):value(v){}
int getVal() { return value; } // IntVal operator+(IntVal rhs)
// {
// IntVal result(value + rhs.value);
// return result;
// } // IntVal operator+(int rhs)
// {
// IntVal result(value + rhs);
// return result;
// } // static IntVal operator+(int lhs, IntVal rhs)
// {
// IntVal result(lhs + rhs.value);
// return result;
// } static operator IntVal(int v)
{
return IntVal(v);
} static IntVal operator+(IntVal lhs, IntVal rhs)
{
IntVal result(lhs.value + rhs.value); return result;
} static bool operator==(IntVal lhs, IntVal rhs)
{
return lhs.value == rhs.value;
} static bool operator!=(IntVal lhs, IntVal rhs)
{
return !(lhs == rhs);
} virtual bool Equals(Object^ other) override
{
IntVal^ obj = dynamic_cast<IntVal^>(other);
if (obj==nullptr)
{
return false;
} return value == obj->value;
} static IntVal operator++(IntVal i)
{
i.value++;
return i;
}
};
ref struct LongVal
{
private:
long value;
public:
LongVal(long v) :value(v) {}
int getVal() { return value; } static operator LongVal^(long l)
{
return gcnew LongVal(l);
} static LongVal^ operator+(LongVal^ lhs, LongVal^ rhs)
{
LongVal^ result = gcnew LongVal(lhs->value + rhs->value); return result;
}
};
int main(array<System::String ^> ^args)
{
//Console::WriteLine(L"Hello World");
IntVal one();
IntVal two();
IntVal three; three = one + two;
Console::WriteLine(three.getVal()); IntVal four;
four = two + ;
Console::WriteLine(four.getVal()); IntVal five;
five = + two;
Console::WriteLine(five.getVal()); IntVal somenum();
if (somenum == )
{
Console::WriteLine(L"somenum == 2");
} if (somenum != )
{
Console::WriteLine(L"somenum != 3");
} IntVal six(), seven();
if (!six.Equals(seven))
{
Console::WriteLine(L"six != seven");
} LongVal^ lone = gcnew LongVal();
LongVal^ ltwo = gcnew LongVal();
LongVal^ lthree = lone + ltwo;
Console::WriteLine(lthree->getVal()); return ;
}
=======================================================================
第十一章 异常处理
类型转换
safe_cast 转换失败抛出异常
dynamic_cast转换失败返回空指针
捕捉System::Exception派生类的对象
C++
构造函数 重载操作符——>异常在这些地方很有用
C++/CLI
传统C++异常
C++/CLI异常
SEH
抛出异常
形式
try
{
if(a<0)
throw gcnew ArgumentException(L"Test by WLS");
}
catch(ArgumentException^ ex)
{
Console::WriteLine(L"Exception caught in Test by WLS");
throw;
}
catch(Exception^)//捕捉所有异常 但会丢失所有异常信息
{
}
创建自己的异常类型
ref class WLSException:System::Exception
{
public:
int ErrorNum;//自定义错误号
WLSException(String^ msg,int num):Exception(msg),ErrorNum(num){}
};
使用时 throw gcnew WLSException(e->Message,666);
=======================================================================
第十二章 数组和集合
用size_t比int要好
泛型类型
generic <typename T>
ref class MyList
{
public:
void Add(T num);
}
枚举器
IEnumerator接口
只能读取 修改还是要用常规循环
迭代器模式 遍历任何集合 不保证顺序
初始位置第一个之前
MoveNext方法 移动到下一个元素,没有就返回false
Current属性 当前项
Reset方法 重置到第一个之前
托管数组
所有的托管数组都继承自System::Array
array<类型,维度> handle_name
托管堆
垃圾回收
索引从0开始 越界访问抛出异常
arr1->Length 数组各维度的长度和
arr1->GetLength(2) 第2维的长度
array<Person^>^ arr3;//支持各种类型 array<int>^ arr1 = { ,, };//C++风格 array<int>^ arr2= gcnew array<int>() {,,}; array<int>^ arr3 = gcnew array<int>() {,,};//为什么这个我的VS2015社区版不通过? error C2748: 创建 托管 数组时必须提供数组大小或数组初始值设定项 array<int, >^ arr1= gcnew array<int, >(,);//二维数组
Console::WriteLine(arr1->Length);
Console::WriteLine(arr1->GetLength());
Console::WriteLine(arr1->GetLength());
for (int i = ;i < arr1->GetLength();i++)
{
for (int j = ;j<arr1->GetLength();j++)
{
arr1[i,j] = i*j;//访问方式好独特,一个“[]”里用“,”分隔各个维度
}
} for (int i = ;i < arr1->GetLength();i++)
{
for (int j = ;j < arr1->GetLength();j++)
{
Console::WriteLine(arr1[i, j]);
}
}
array<String^>^ arr = gcnew array<String^>(SIZE) {gcnew String(L"abc"), gcnew String(L"def")};//初始化 array<String^>^ arrs = gcnew array<String^>(SIZE);
arrs[] = gcnew String(L"abc");
arrs[] = L"def";
//多维数组
array<int, >^ arr1 = { {,,},{,,} };
for (int i = ;i < arr1->GetLength();i++)
{
for (int j = ;j < arr1->GetLength();j++)
{
Console::WriteLine(arr1[i, j]);
}
} array<int, >^ arr2 = gcnew array<int, >{ {, , }, { ,, }};
for (int i = ;i < arr2->GetLength();i++)
{
for (int j = ;j < arr2->GetLength();j++)
{
Console::WriteLine(arr2[i, j]);
}
}
怎么在多维数组中使用foreach来遍历其中的某一维呢?
例如遍历array<String^, 2>^ arr3 = gcnew array<String^, 2>{ {L"aa", L"bb", L"cc"}, { L"ee",L"ff",L"gg" }};的第二维的元素?
=======================================================================
第十三章 属性
C++/CLI支持两种属性
标量属性 通过取值赋值访问单个值 属性不一定是数据成员可以是导出值
索引属性 使用[]来访问属性
关键字 property
构造函数里应该优先使用属性进行初始化而不是直接使用数据成员
可以在属性里使用throw抛出异常
自动实现属性
property String^ Name;//默认实现get和set
只读或只写属性
property String^ Name
{
//只实现get或者set
String^ g/set(){...}
}
继承、接口
属性可以是virtual也可以是纯virtual的,可以在继承和接口中使用
ref class CShape abstract
{
public:
virtual property double Area;
}; ref class CCricle:CShape
{
public: CCricle(double r) { m_nRadius = r; } virtual property double Area
{
double get() override
{
return Math::PI*m_nRadius*m_nRadius;
}
} void PrintArea()
{
Console::WriteLine(L"The Area is {0}",Area);
} private:
double m_nRadius;
}; int main(array<System::String ^> ^args)
{
CCricle^ oCricle = gcnew CCricle(4.0);
oCricle->PrintArea(); return ;
}
索引属性
默认属性
类可以有多个索引器(索引属性),必须根据名称显示的使用。(下面的oBank->Balance[234567])
名为default的索引属性可以在类对象上直接使用。(下面的CAccount^ pA = oBank[234567])
#include "stdafx.h" using namespace System;
using namespace System::Collections::Generic; //////////////////////////////////////////////////////////////////////////
ref class CAccount
{
public:
CAccount(long lAccNum,double dBalance,double dLimit);
~CAccount(); property long AccountNumber
{
long get() { return m_lAccNumber; }
} property double Balance
{
double get() { return m_dBalance; }
} property double OverdraftLimit
{
double get() { return m_dLimit; }
void set(double dValue)
{
if (dValue<)
{
throw gcnew ArgumentException(L"Limit can not be negative");
} m_dLimit = dValue;
}
} private:
long m_lAccNumber;
double m_dBalance;
double m_dLimit;
}; CAccount::CAccount(long lAccNum, double dBalance, double dLimit)
{
Console::WriteLine(L"Account:Constructor"); if (lAccNum< || dLimit<)
{
throw gcnew ArgumentException(L"Bad Arguments to constructor");
} m_lAccNumber = lAccNum;
m_dBalance = dBalance;
m_dLimit = dLimit;
} CAccount::~CAccount()
{
}
//////////////////////////////////////////////////////////////////////////
ref class CBank
{
public:
CBank();
~CBank(); bool AddAccount(CAccount^ oAccount)
{
if (m_listAccounts->Contains(oAccount))
{
return false;
}
else
{
m_listAccounts->Add(oAccount);
} return true;
} bool RemoveAccount(CAccount^ oAccount)
{
if (m_listAccounts->Contains(oAccount))
{
m_listAccounts->Remove(oAccount); return true;
} return false;
} property double Balance[long]
{
double get(long lIndex)
{
for each (CAccount^ var in m_listAccounts)
{
if (var->AccountNumber==lIndex)
{
return var->Balance;
}
} throw gcnew ArgumentOutOfRangeException(L"No Such Account");
}
} property CAccount^ default[long]
{
CAccount^ get(long lIndex)
{
for each (CAccount^ var in m_listAccounts)
{
if (var->AccountNumber == lIndex)
{
return var;
}
} throw gcnew ArgumentOutOfRangeException(L"No Such Account");
}
}
private:
List<CAccount^>^ m_listAccounts;
}; CBank::CBank()
{
Console::WriteLine(L"Bank:Constructor"); m_listAccounts = gcnew List<CAccount^>();
} CBank::~CBank()
{
} int main(array<System::String ^> ^args)
{
CBank^ oBank = gcnew CBank(); CAccount^ oAccount1 = gcnew CAccount(, 10.0, 0.0);
CAccount^ oAccount2 = gcnew CAccount(, 110.0, 10.0);
CAccount^ oAccount3 = gcnew CAccount(, 1110.0, 110.0); oBank->AddAccount(oAccount1);
oBank->AddAccount(oAccount2);
oBank->AddAccount(oAccount3); CAccount^ pA = oBank[];
Console::WriteLine(L"The Account Number {0} has the banlance {1}.", pA->AccountNumber,oBank->Balance[]); return ;
}
(为什么我感觉我的代码写的有点问题,虽然可以跑?Balance属性真的这么写吗?)
=======================================================================
第十四章 委托和事件
委托是特殊的类
原理是将函数的执行委托给一个中间对象,调用具有特定签名的一个或者多个函数。
C++/CLI的所有委托都是System::MulticastDelegate。
关键字 delegate
delegate double SomeOperating(double);
只能调用托管类的成员函数(静态非静态都可以)。
静态成员函数只需要传递函数地址
非静态成员函数需要传递对象和函数地址
委托创建好后不能改变调用的函数,但是可以重新gcnew一个新的,垃圾自动回收。
调用委托可以使用invoke也可以使用委托的仿函数。
MulticastDelegate使用Combine和Remove来操作调用列表。
MulticastDelegate使用合并其他委托的方法来生成。
MulticastDelegate的调用顺序由合并顺序决定。
MulticastDelegate通常用不返回值的函数,但也可以返回值,一般是最后一个的结果。想要获得某个结果可以遍历委托列表。
#include "stdafx.h" using namespace System; delegate double NumbericOp1(double);
delegate double NumbericOp2(double, double); ref class Ops
{
public:
static double Square(double dNum) { return dNum*dNum; }
static double Cube(double dNum) { return dNum*dNum*dNum; } double MultiAandB(double dNum1, double dNum2) { return dNum1*dNum2; }
}; int main(array<System::String ^> ^args)
{
NumbericOp1^ NOp1 = gcnew NumbericOp1(Ops::Square);//静态函数//编译通过了 double dTempNum1 = 10.0; Console::WriteLine(L"Square({0}) = {1}",dTempNum1,NOp1->Invoke(dTempNum1));
Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1(dTempNum1)); NOp1 = gcnew NumbericOp1(Ops::Cube);//静态函数//编译通过了 Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1->Invoke(dTempNum1));
Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1(dTempNum1)); //////////////////////////////////////////////////////////////////////////
double dTempNum2 = 2.0;
double dTempNum3 = 3.0; Ops^ objOps=gcnew Ops(); NumbericOp2^ NOp2;
NOp2 = gcnew NumbericOp2(objOps, &Ops::MultiAandB);//非静态函数 Console::WriteLine(L"multi({0},{1})={2}", dTempNum2,dTempNum3,NOp2->Invoke(dTempNum2,dTempNum3));
Console::WriteLine(L"multi({0},{1})={2}", dTempNum2, dTempNum3, NOp2(dTempNum2, dTempNum3)); return ;
}
#include "stdafx.h" using namespace System; delegate void PrintSomething(int); delegate int DOperation(int); ref class CClient1
{
public:
static void Print(int nNum) { Console::WriteLine(L"CClient1 {0}",nNum); }
int DoubleNum(int nNum) { return nNum<<; }
}; ref class CClient2
{
public:
static void Print(int nNum) { Console::WriteLine(L"CClient2 {0}",nNum); }
int DDoubleNum(int nNum) { return nNum << ; }
}; int main(array<System::String ^> ^args)
{
PrintSomething^ PS1=gcnew PrintSomething(CClient1::Print);
PrintSomething^ PS2=gcnew PrintSomething(CClient2::Print);
PrintSomething^ PS3;
PS3+= PS1+PS2;
PS3(); Console::WriteLine(L"-------------");
PS3 += PS3+PS3;
PS3(); Console::WriteLine(L"-------------");
PrintSomething^ PS4;
PS4 = PS3 + PS3;
PS4(); Console::WriteLine(L"-------------");
PS4 -= PS3;
PS4(); CClient1^ oC1 = gcnew CClient1();
CClient2^ oC2 = gcnew CClient2(); DOperation^ DOp;
DOp = gcnew DOperation(oC1, &CClient1::DoubleNum)+gcnew DOperation(oC2,&CClient2::DDoubleNum); for each (DOperation^ dop in DOp->GetInvocationList())
{
Console::WriteLine(dop());
} return ;
}
.net的事件
发布-订阅 机制
基于委托
事件只能由声明它的类型引发
客户端只能用+=和-=来增删事件处理函数,不能用=重置调用列表
事件源声明委托
事件接收者提供适当方法
方法绑定到委托
事件发生调用委托进而调用方法
#include "stdafx.h" using namespace System; //////////////////////////////////////////////////////////////////////////
delegate void FristEventHandler(String^);
delegate void SecondEventHandle(String^); ref class EventSrc
{
public:
event FristEventHandler^ OnFirstEvent;
event SecondEventHandle^ OnSecondEvent; void RaiseOne(String^ msg) { OnFirstEvent(msg); }
void RaiseTwo(String^ msg) { OnSecondEvent(msg); }
};
//////////////////////////////////////////////////////////////////////////
ref class EventReceiver
{
public:
EventReceiver(EventSrc^ esrc)
{
if (esrc==nullptr)
{
throw gcnew ArgumentException(L"Must have event source");
} m_EventSrc = esrc; m_EventSrc->OnFirstEvent += gcnew FristEventHandler(this, &EventReceiver::DoforFirstEvent);
m_EventSrc->OnSecondEvent += gcnew SecondEventHandle(this, &EventReceiver::DoforSecondEvent);
} void RemoveHandler()
{
m_EventSrc->OnFirstEvent -= gcnew FristEventHandler(this, &EventReceiver::DoforFirstEvent);
} void DoforFirstEvent(String^ msg) { Console::WriteLine(L"[Eventreceiver] event one,message {0}", msg); }
void DoforSecondEvent(String^ msg) { Console::WriteLine(L"[Eventreceiver] event two,message {0}", msg); } private:
EventSrc^ m_EventSrc;
}; int main(array<System::String ^> ^args)
{
EventSrc^ src = gcnew EventSrc();
EventReceiver^ recvr = gcnew EventReceiver(src); src->RaiseOne(L"Hahaha");
src->RaiseTwo(L"blablabla"); Console::WriteLine();
recvr->RemoveHandler(); src->RaiseOne(L"Hahaha");
src->RaiseTwo(L"blablabla"); return ;
}
标准事件 System::EventHandler
签名 delegate void EventHandler(System::Object^ sender, System::EventArgs^ e)
建议使用标准事件
直接使用System::EventHandler
#include "stdafx.h" using namespace System; //////////////////////////////////////////////////////////////////////////
ref class CCounter
{
public:
CCounter(int nLimit)
{
m_nCounter = ;
m_nLimit = nLimit;
} event EventHandler^ LimitReached; void Increment()
{
Console::WriteLine(L"Count:{0}",++m_nCounter); if (m_nCounter % m_nLimit==)
{
LimitReached(this, gcnew EventArgs());
}
} private:
int m_nCounter;
int m_nLimit;
}; //////////////////////////////////////////////////////////////////////////
ref class CObserver
{
public:
static void Callme(Object^ src, EventArgs^ args)
{
Console::WriteLine(L"Limit reached");
} void CallmeBaby(Object^ src, EventArgs^ args)
{
Console::WriteLine(L"Oh,Honey!");
} }; //////////////////////////////////////////////////////////////////////////
int main(array<System::String ^> ^args)
{
//////////////////////////////////////////////////////////////////////////
CCounter^ oCounter=gcnew CCounter(); //////////////////////////////////////////////////////////////////////////
oCounter->LimitReached += gcnew EventHandler(&CObserver::Callme); for (int i = ;i<;i++)
{
oCounter->Increment();
} //////////////////////////////////////////////////////////////////////////
//oCounter->LimitReached -= gcnew EventHandler(&CObserver::Callme);
////////////////////////////////////////////////////////////////////////// CObserver^ oObserver=gcnew CObserver(); oCounter->LimitReached += gcnew EventHandler(oObserver, &CObserver::CallmeBaby); for (int i = ;i < ;i++)
{
oCounter->Increment();
} return ;
}
=======================================================================
第十五章 .NET Framework类库
鼓吹.NET的一章,泛泛草草的说了几页,意思大概就是之前的十四章都看完了,接着往下看吧,好戏才刚开始呢。
=======================================================================
第Ⅱ部分也没难度,只是容易忘、记不住。
=======================================================================
说说书上的错误吧
Page75 6.5 在类中使用常量
第一个小圆点· “它的值对于Card的所有实例来说都是4”,很显然英文拼错了,正确是“Car”
Page174 12.3.1 初始化
一共有三行代码,第二行的代码不能有(),应该改为array<int>^ intArray=gcnew array<int>{1,2,3};
Page207 14.2.2 使用MulticastDelegate
“而对于MutlcastDelegate,可使用……”中的英文显然拼错了,正确是“MulticastDelegate”
=======================================================================
再说说一点瑕疵
Page195 13.2.4 属性、继承和接口
在练习中给出的托管类定义,ref的前面是不需要public的,至少在这个例子和当前的教学上下文是不需要的。
=======================================================================
有空开始看第Ⅲ部分