=================================版权声明=================================
版权声明:本文为博主原创文章 未经许可不得转载
请通过右侧公告中的“联系邮箱(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;
}
1 value struct IntVal 2 { 3 private: 4 int value; 5 public: 6 IntVal(int v):value(v){} 7 int getVal() { return value; } 8 9 // IntVal operator+(IntVal rhs) 10 // { 11 // IntVal result(value + rhs.value); 12 // return result; 13 // } 14 15 // IntVal operator+(int rhs) 16 // { 17 // IntVal result(value + rhs); 18 // return result; 19 // } 20 21 // static IntVal operator+(int lhs, IntVal rhs) 22 // { 23 // IntVal result(lhs + rhs.value); 24 // return result; 25 // } 26 27 static operator IntVal(int v) 28 { 29 return IntVal(v); 30 } 31 32 static IntVal operator+(IntVal lhs, IntVal rhs) 33 { 34 IntVal result(lhs.value + rhs.value); 35 36 return result; 37 } 38 39 static bool operator==(IntVal lhs, IntVal rhs) 40 { 41 return lhs.value == rhs.value; 42 } 43 44 static bool operator!=(IntVal lhs, IntVal rhs) 45 { 46 return !(lhs == rhs); 47 } 48 49 virtual bool Equals(Object^ other) override 50 { 51 IntVal^ obj = dynamic_cast<IntVal^>(other); 52 if (obj==nullptr) 53 { 54 return false; 55 } 56 57 return value == obj->value; 58 } 59 60 static IntVal operator++(IntVal i) 61 { 62 i.value++; 63 return i; 64 } 65 };
1 ref struct LongVal 2 { 3 private: 4 long value; 5 public: 6 LongVal(long v) :value(v) {} 7 int getVal() { return value; } 8 9 static operator LongVal^(long l) 10 { 11 return gcnew LongVal(l); 12 } 13 14 static LongVal^ operator+(LongVal^ lhs, LongVal^ rhs) 15 { 16 LongVal^ result = gcnew LongVal(lhs->value + rhs->value); 17 18 return result; 19 } 20 };
1 int main(array<System::String ^> ^args) 2 { 3 //Console::WriteLine(L"Hello World"); 4 IntVal one(1); 5 IntVal two(2); 6 IntVal three; 7 8 three = one + two; 9 Console::WriteLine(three.getVal()); 10 11 IntVal four; 12 four = two + 2; 13 Console::WriteLine(four.getVal()); 14 15 IntVal five; 16 five = 3 + two; 17 Console::WriteLine(five.getVal()); 18 19 IntVal somenum(2); 20 if (somenum == 2) 21 { 22 Console::WriteLine(L"somenum == 2"); 23 } 24 25 if (somenum != 3) 26 { 27 Console::WriteLine(L"somenum != 3"); 28 } 29 30 IntVal six(6), seven(7); 31 if (!six.Equals(seven)) 32 { 33 Console::WriteLine(L"six != seven"); 34 } 35 36 LongVal^ lone = gcnew LongVal(1); 37 LongVal^ ltwo = gcnew LongVal(2); 38 LongVal^ lthree = lone + ltwo; 39 Console::WriteLine(lthree->getVal()); 40 41 return 0; 42 }
=======================================================================
第十一章 异常处理
类型转换
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维的长度
1 array<Person^>^ arr3;//支持各种类型 2 3 array<int>^ arr1 = { 1,2,3 };//C++风格 4 5 array<int>^ arr2= gcnew array<int>(3) {4,5,6}; 6 7 array<int>^ arr3 = gcnew array<int>() {7,8,9};//为什么这个我的VS2015社区版不通过? error C2748: 创建 托管 数组时必须提供数组大小或数组初始值设定项 8 9 array<int, 2>^ arr1= gcnew array<int, 2>(3,3);//二维数组 10 Console::WriteLine(arr1->Length); 11 Console::WriteLine(arr1->GetLength(0)); 12 Console::WriteLine(arr1->GetLength(1)); 13 for (int i = 0;i < arr1->GetLength(0);i++) 14 { 15 for (int j = 0;j<arr1->GetLength(1);j++) 16 { 17 arr1[i,j] = i*j;//访问方式好独特,一个“[]”里用“,”分隔各个维度 18 } 19 } 20 21 for (int i = 0;i < arr1->GetLength(0);i++) 22 { 23 for (int j = 0;j < arr1->GetLength(1);j++) 24 { 25 Console::WriteLine(arr1[i, j]); 26 } 27 }
1 array<String^>^ arr = gcnew array<String^>(SIZE) {gcnew String(L"abc"), gcnew String(L"def")};//初始化 2 3 array<String^>^ arrs = gcnew array<String^>(SIZE); 4 arrs[0] = gcnew String(L"abc"); 5 arrs[1] = L"def";
1 //多维数组 2 array<int, 2>^ arr1 = { {1,2,3},{4,5,6} }; 3 for (int i = 0;i < arr1->GetLength(0);i++) 4 { 5 for (int j = 0;j < arr1->GetLength(1);j++) 6 { 7 Console::WriteLine(arr1[i, j]); 8 } 9 } 10 11 array<int, 2>^ arr2 = gcnew array<int, 2>{ {7, 8, 9}, { 10,11,12 }}; 12 for (int i = 0;i < arr2->GetLength(0);i++) 13 { 14 for (int j = 0;j < arr2->GetLength(1);j++) 15 { 16 Console::WriteLine(arr2[i, j]); 17 } 18 }
怎么在多维数组中使用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的,可以在继承和接口中使用
1 ref class CShape abstract 2 { 3 public: 4 virtual property double Area; 5 }; 6 7 ref class CCricle:CShape 8 { 9 public: 10 11 CCricle(double r) { m_nRadius = r; } 12 13 virtual property double Area 14 { 15 double get() override 16 { 17 return Math::PI*m_nRadius*m_nRadius; 18 } 19 } 20 21 void PrintArea() 22 { 23 Console::WriteLine(L"The Area is {0}",Area); 24 } 25 26 private: 27 double m_nRadius; 28 }; 29 30 31 32 int main(array<System::String ^> ^args) 33 { 34 CCricle^ oCricle = gcnew CCricle(4.0); 35 oCricle->PrintArea(); 36 37 return 0; 38 }
索引属性
默认属性
类可以有多个索引器(索引属性),必须根据名称显示的使用。(下面的oBank->Balance[234567])
名为default的索引属性可以在类对象上直接使用。(下面的CAccount^ pA = oBank[234567])
1 #include "stdafx.h" 2 3 using namespace System; 4 using namespace System::Collections::Generic; 5 6 ////////////////////////////////////////////////////////////////////////// 7 ref class CAccount 8 { 9 public: 10 CAccount(long lAccNum,double dBalance,double dLimit); 11 ~CAccount(); 12 13 property long AccountNumber 14 { 15 long get() { return m_lAccNumber; } 16 } 17 18 property double Balance 19 { 20 double get() { return m_dBalance; } 21 } 22 23 property double OverdraftLimit 24 { 25 double get() { return m_dLimit; } 26 void set(double dValue) 27 { 28 if (dValue<0) 29 { 30 throw gcnew ArgumentException(L"Limit can not be negative"); 31 } 32 33 m_dLimit = dValue; 34 } 35 } 36 37 private: 38 long m_lAccNumber; 39 double m_dBalance; 40 double m_dLimit; 41 }; 42 43 CAccount::CAccount(long lAccNum, double dBalance, double dLimit) 44 { 45 Console::WriteLine(L"Account:Constructor"); 46 47 if (lAccNum<0 || dLimit<0) 48 { 49 throw gcnew ArgumentException(L"Bad Arguments to constructor"); 50 } 51 52 m_lAccNumber = lAccNum; 53 m_dBalance = dBalance; 54 m_dLimit = dLimit; 55 } 56 57 CAccount::~CAccount() 58 { 59 } 60 ////////////////////////////////////////////////////////////////////////// 61 ref class CBank 62 { 63 public: 64 CBank(); 65 ~CBank(); 66 67 bool AddAccount(CAccount^ oAccount) 68 { 69 if (m_listAccounts->Contains(oAccount)) 70 { 71 return false; 72 } 73 else 74 { 75 m_listAccounts->Add(oAccount); 76 } 77 78 return true; 79 } 80 81 bool RemoveAccount(CAccount^ oAccount) 82 { 83 if (m_listAccounts->Contains(oAccount)) 84 { 85 m_listAccounts->Remove(oAccount); 86 87 return true; 88 } 89 90 return false; 91 } 92 93 property double Balance[long] 94 { 95 double get(long lIndex) 96 { 97 for each (CAccount^ var in m_listAccounts) 98 { 99 if (var->AccountNumber==lIndex) 100 { 101 return var->Balance; 102 } 103 } 104 105 throw gcnew ArgumentOutOfRangeException(L"No Such Account"); 106 } 107 } 108 109 property CAccount^ default[long] 110 { 111 CAccount^ get(long lIndex) 112 { 113 for each (CAccount^ var in m_listAccounts) 114 { 115 if (var->AccountNumber == lIndex) 116 { 117 return var; 118 } 119 } 120 121 throw gcnew ArgumentOutOfRangeException(L"No Such Account"); 122 } 123 } 124 private: 125 List<CAccount^>^ m_listAccounts; 126 }; 127 128 CBank::CBank() 129 { 130 Console::WriteLine(L"Bank:Constructor"); 131 132 m_listAccounts = gcnew List<CAccount^>(); 133 } 134 135 CBank::~CBank() 136 { 137 } 138 139 140 int main(array<System::String ^> ^args) 141 { 142 CBank^ oBank = gcnew CBank(); 143 144 CAccount^ oAccount1 = gcnew CAccount(123456, 10.0, 0.0); 145 CAccount^ oAccount2 = gcnew CAccount(234567, 110.0, 10.0); 146 CAccount^ oAccount3 = gcnew CAccount(345678, 1110.0, 110.0); 147 148 oBank->AddAccount(oAccount1); 149 oBank->AddAccount(oAccount2); 150 oBank->AddAccount(oAccount3); 151 152 CAccount^ pA = oBank[234567]; 153 Console::WriteLine(L"The Account Number {0} has the banlance {1}.", pA->AccountNumber,oBank->Balance[234567]); 154 155 return 0; 156 }
(为什么我感觉我的代码写的有点问题,虽然可以跑?Balance属性真的这么写吗?)
=======================================================================
第十四章 委托和事件
委托是特殊的类
原理是将函数的执行委托给一个中间对象,调用具有特定签名的一个或者多个函数。
C++/CLI的所有委托都是System::MulticastDelegate。
关键字 delegate
delegate double SomeOperating(double);
只能调用托管类的成员函数(静态非静态都可以)。
静态成员函数只需要传递函数地址
非静态成员函数需要传递对象和函数地址
委托创建好后不能改变调用的函数,但是可以重新gcnew一个新的,垃圾自动回收。
调用委托可以使用invoke也可以使用委托的仿函数。
MulticastDelegate使用Combine和Remove来操作调用列表。
MulticastDelegate使用合并其他委托的方法来生成。
MulticastDelegate的调用顺序由合并顺序决定。
MulticastDelegate通常用不返回值的函数,但也可以返回值,一般是最后一个的结果。想要获得某个结果可以遍历委托列表。
1 #include "stdafx.h" 2 3 using namespace System; 4 5 delegate double NumbericOp1(double); 6 delegate double NumbericOp2(double, double); 7 8 ref class Ops 9 { 10 public: 11 static double Square(double dNum) { return dNum*dNum; } 12 static double Cube(double dNum) { return dNum*dNum*dNum; } 13 14 double MultiAandB(double dNum1, double dNum2) { return dNum1*dNum2; } 15 }; 16 17 int main(array<System::String ^> ^args) 18 { 19 NumbericOp1^ NOp1 = gcnew NumbericOp1(Ops::Square);//静态函数//编译通过了 20 21 double dTempNum1 = 10.0; 22 23 Console::WriteLine(L"Square({0}) = {1}",dTempNum1,NOp1->Invoke(dTempNum1)); 24 Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1(dTempNum1)); 25 26 NOp1 = gcnew NumbericOp1(Ops::Cube);//静态函数//编译通过了 27 28 Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1->Invoke(dTempNum1)); 29 Console::WriteLine(L"Square({0}) = {1}", dTempNum1, NOp1(dTempNum1)); 30 31 ////////////////////////////////////////////////////////////////////////// 32 double dTempNum2 = 2.0; 33 double dTempNum3 = 3.0; 34 35 Ops^ objOps=gcnew Ops(); 36 37 NumbericOp2^ NOp2; 38 NOp2 = gcnew NumbericOp2(objOps, &Ops::MultiAandB);//非静态函数 39 40 Console::WriteLine(L"multi({0},{1})={2}", dTempNum2,dTempNum3,NOp2->Invoke(dTempNum2,dTempNum3)); 41 Console::WriteLine(L"multi({0},{1})={2}", dTempNum2, dTempNum3, NOp2(dTempNum2, dTempNum3)); 42 43 return 0; 44 }
1 #include "stdafx.h" 2 3 using namespace System; 4 5 delegate void PrintSomething(int); 6 7 delegate int DOperation(int); 8 9 ref class CClient1 10 { 11 public: 12 static void Print(int nNum) { Console::WriteLine(L"CClient1 {0}",nNum); } 13 int DoubleNum(int nNum) { return nNum<<1; } 14 }; 15 16 ref class CClient2 17 { 18 public: 19 static void Print(int nNum) { Console::WriteLine(L"CClient2 {0}",nNum); } 20 int DDoubleNum(int nNum) { return nNum << 2; } 21 }; 22 23 24 int main(array<System::String ^> ^args) 25 { 26 PrintSomething^ PS1=gcnew PrintSomething(CClient1::Print); 27 PrintSomething^ PS2=gcnew PrintSomething(CClient2::Print); 28 PrintSomething^ PS3; 29 PS3+= PS1+PS2; 30 PS3(2333); 31 32 Console::WriteLine(L"-------------"); 33 PS3 += PS3+PS3; 34 PS3(233); 35 36 Console::WriteLine(L"-------------"); 37 PrintSomething^ PS4; 38 PS4 = PS3 + PS3; 39 PS4(23333); 40 41 Console::WriteLine(L"-------------"); 42 PS4 -= PS3; 43 PS4(233333); 44 45 CClient1^ oC1 = gcnew CClient1(); 46 CClient2^ oC2 = gcnew CClient2(); 47 48 DOperation^ DOp; 49 DOp = gcnew DOperation(oC1, &CClient1::DoubleNum)+gcnew DOperation(oC2,&CClient2::DDoubleNum); 50 51 for each (DOperation^ dop in DOp->GetInvocationList()) 52 { 53 Console::WriteLine(dop(666)); 54 } 55 56 return 0; 57 }
.net的事件
发布-订阅 机制
基于委托
事件只能由声明它的类型引发
客户端只能用+=和-=来增删事件处理函数,不能用=重置调用列表
事件源声明委托
事件接收者提供适当方法
方法绑定到委托
事件发生调用委托进而调用方法
1 #include "stdafx.h" 2 3 using namespace System; 4 5 ////////////////////////////////////////////////////////////////////////// 6 delegate void FristEventHandler(String^); 7 delegate void SecondEventHandle(String^); 8 9 ref class EventSrc 10 { 11 public: 12 event FristEventHandler^ OnFirstEvent; 13 event SecondEventHandle^ OnSecondEvent; 14 15 void RaiseOne(String^ msg) { OnFirstEvent(msg); } 16 void RaiseTwo(String^ msg) { OnSecondEvent(msg); } 17 }; 18 ////////////////////////////////////////////////////////////////////////// 19 ref class EventReceiver 20 { 21 public: 22 EventReceiver(EventSrc^ esrc) 23 { 24 if (esrc==nullptr) 25 { 26 throw gcnew ArgumentException(L"Must have event source"); 27 } 28 29 m_EventSrc = esrc; 30 31 m_EventSrc->OnFirstEvent += gcnew FristEventHandler(this, &EventReceiver::DoforFirstEvent); 32 m_EventSrc->OnSecondEvent += gcnew SecondEventHandle(this, &EventReceiver::DoforSecondEvent); 33 } 34 35 void RemoveHandler() 36 { 37 m_EventSrc->OnFirstEvent -= gcnew FristEventHandler(this, &EventReceiver::DoforFirstEvent); 38 } 39 40 void DoforFirstEvent(String^ msg) { Console::WriteLine(L"[Eventreceiver] event one,message {0}", msg); } 41 void DoforSecondEvent(String^ msg) { Console::WriteLine(L"[Eventreceiver] event two,message {0}", msg); } 42 43 private: 44 EventSrc^ m_EventSrc; 45 }; 46 47 int main(array<System::String ^> ^args) 48 { 49 EventSrc^ src = gcnew EventSrc(); 50 EventReceiver^ recvr = gcnew EventReceiver(src); 51 52 src->RaiseOne(L"Hahaha"); 53 src->RaiseTwo(L"blablabla"); 54 55 Console::WriteLine(); 56 recvr->RemoveHandler(); 57 58 src->RaiseOne(L"Hahaha"); 59 src->RaiseTwo(L"blablabla"); 60 61 return 0; 62 } 63
标准事件 System::EventHandler
签名 delegate void EventHandler(System::Object^ sender, System::EventArgs^ e)
建议使用标准事件
直接使用System::EventHandler
1 #include "stdafx.h" 2 3 using namespace System; 4 5 ////////////////////////////////////////////////////////////////////////// 6 ref class CCounter 7 { 8 public: 9 CCounter(int nLimit) 10 { 11 m_nCounter = 0; 12 m_nLimit = nLimit; 13 } 14 15 event EventHandler^ LimitReached; 16 17 void Increment() 18 { 19 Console::WriteLine(L"Count:{0}",++m_nCounter); 20 21 if (m_nCounter % m_nLimit==0) 22 { 23 LimitReached(this, gcnew EventArgs()); 24 } 25 } 26 27 private: 28 int m_nCounter; 29 int m_nLimit; 30 }; 31 32 ////////////////////////////////////////////////////////////////////////// 33 ref class CObserver 34 { 35 public: 36 static void Callme(Object^ src, EventArgs^ args) 37 { 38 Console::WriteLine(L"Limit reached"); 39 } 40 41 void CallmeBaby(Object^ src, EventArgs^ args) 42 { 43 Console::WriteLine(L"Oh,Honey!"); 44 } 45 46 }; 47 48 ////////////////////////////////////////////////////////////////////////// 49 int main(array<System::String ^> ^args) 50 { 51 ////////////////////////////////////////////////////////////////////////// 52 CCounter^ oCounter=gcnew CCounter(5); 53 54 ////////////////////////////////////////////////////////////////////////// 55 oCounter->LimitReached += gcnew EventHandler(&CObserver::Callme); 56 57 for (int i = 0;i<11;i++) 58 { 59 oCounter->Increment(); 60 } 61 62 ////////////////////////////////////////////////////////////////////////// 63 //oCounter->LimitReached -= gcnew EventHandler(&CObserver::Callme); 64 ////////////////////////////////////////////////////////////////////////// 65 66 CObserver^ oObserver=gcnew CObserver(); 67 68 oCounter->LimitReached += gcnew EventHandler(oObserver, &CObserver::CallmeBaby); 69 70 for (int i = 0;i < 11;i++) 71 { 72 oCounter->Increment(); 73 } 74 75 return 0; 76 }
=======================================================================
第十五章 .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的,至少在这个例子和当前的教学上下文是不需要的。
=======================================================================
有空开始看第Ⅲ部分