C++/CLI——读书笔记《Visual C++/CLI从入门到精通》 第Ⅱ部分

=================================版权声明=================================

版权声明:本文为博主原创文章 未经许可不得转载 

请通过右侧公告中的“联系邮箱(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的,至少在这个例子和当前的教学上下文是不需要的。

=======================================================================

有空开始看第Ⅲ部分

上一篇:Angular学习笔记:Angular CLI


下一篇:认识CLR [《CLR via C#》读书笔记]