//////////////////////////////////////////////////////////////////////////////// //9.6.7 虚析构函数 /*#include "stdafx.h" #include <iostream> using namespace std; using namespace System; const double PI = 3.14159265; //定义纯虚函数 class CContainer { public: virtual double Volume() const = 0; //定义虚函数Volume()的语句通过在函数头中添加等号和0,将该函数定义成没有任何内容,这样的函数被称为纯虚函数 virtual void ShowVolume() const { cout<<endl <<"Volume is"<<Volume()<<endl; } virtual ~CContainer(){ cout<<"CContainer 析构函数执行....."<<endl; } //一个问题:可以将构造函数声明为virtual吗?答案是不行的,只有析构孙函数和其它成员函数才可以 //当使用继承时,总是照例将基类的析构函数声明为虚函数是个好主意, //在为析构函数的执行过程中有少量的系统开稍,但大多数情况下不用理会这一点,使用虚析构函数能够确保正确的销毁对像, //还能避免相反情形下可能出现的程序崩溃风险 }; class CBox : public CContainer { public: virtual void ShowVolume() const { cout<<endl <<"CBox usable volume is "<<Volume(); } virtual double Volume() const { return m_Width * m_Height * m_Length; } CBox(double lv=1.0, double wv=1.0, double hv=1.0):m_Length(lv), m_Width(wv), m_Height(hv) { } ~CBox(){ cout<<"CBox析构函数执行......"<<endl; } protected: double m_Width; double m_Height; double m_Length; }; class CGlassBox : public CBox { public: virtual double Volume() const { return 0.85 * m_Width * m_Height * m_Length; } CGlassBox(double lv, double wv, double hv):CBox(lv, wv, hv) { } ~CGlassBox() { cout<<"CGlassBox析构函数执行......"<<endl; } private: }; //CCan类-它表示啤酒或可乐罐 //ectern const double PI; class CCan: public CContainer { public: virtual double Volume() const { return 0.25*PI*m_Diameter*m_Height; } CCan(double hv=4.0, double dv=2.0):m_Height(hv), m_Diameter(dv) { } ~CCan() { cout<<"CCan析构函数执行......"<<endl; } private: double m_Height; double m_Diameter; }; int main(array<System::String ^> ^args) { CContainer* pC1 = new CBox(2.0,3.0, 4.0); CCan myCan(6.5, 3.0); CGlassBox myGlassBox(2.0, 3.0, 4.0); pC1->ShowVolume(); cout<<endl<<" delete CBox"<<endl; delete pC1; //CBox pC1 = new CGlassBox(4.0, 5.0, 6.0); pC1->ShowVolume(); cout<<endl<<" delete CGlassBox"<<endl; delete pC1; //录我们删除pC1指向的CBox对像时,基类CCOntainer的析构函数被调用,但却没有调用CBox类的析构函数 //同样,当我们删除添加的CGlassBox对像时,还是基类CCOntainer的析构函数被调用 //这里跟书上的实例不太一样,析构函数的用情况是正确遥,都是先调类的析构函数,再调用基类的析构函数,对于在声明中创建的第一个人CGalssBox对像来说,被调用的析构函数有三个 //首先是派生类的析构函数,然后是直接基类的析构函数,最后是间接的基类的析构函数 pC1 = &myCan; pC1->ShowVolume(); pC1 = &myGlassBox; pC1->ShowVolume(); //delete pC1; //这里为什么最后不delete pC1了 //是因为程序一运行结束,动态内存也就自动释放了吗??? //delete pC1; cout<<endl; //由于派生类对像是动态创建的,因此我们不需要它们时必须使用delete运算符清理自由存储器 system("pause"); return 0; } */ //9.7 类类之间的强制转换 //dynamic_cast运算行就是专门执行此类操作的 /*#include "stdafx.h" #include <iostream> using namespace std; using namespace System; const double PI = 3.14159265; //定义纯虚函数 class CContainer { public: virtual double Volume() const = 0; //定义虚函数Volume()的语句通过在函数头中添加等号和0,将该函数定义成没有任何内容,这样的函数被称为纯虚函数 virtual void ShowVolume() const { cout<<endl <<"Volume is"<<Volume()<<endl; } virtual ~CContainer(){ cout<<"CContainer 析构函数执行....."<<endl; } //一个问题:可以将构造函数声明为virtual吗?答案是不行的,只有析构孙函数和其它成员函数才可以 //当使用继承时,总是照例将基类的析构函数声明为虚函数是个好主意, //在为析构函数的执行过程中有少量的系统开稍,但大多数情况下不用理会这一点,使用虚析构函数能够确保正确的销毁对像, //还能避免相反情形下可能出现的程序崩溃风险 }; class CBox : public CContainer { public: virtual void ShowVolume() const { cout<<endl <<"CBox usable volume is "<<Volume(); } virtual double Volume() const { return m_Width * m_Height * m_Length; } CBox(double lv=1.0, double wv=1.0, double hv=1.0):m_Length(lv), m_Width(wv), m_Height(hv) { } ~CBox(){ cout<<"CBox析构函数执行......"<<endl; } protected: double m_Width; double m_Height; double m_Length; }; class CGlassBox : public CBox { public: virtual double Volume() const { return 0.85 * m_Width * m_Height * m_Length; } CGlassBox(double lv, double wv, double hv):CBox(lv, wv, hv) { } ~CGlassBox() { cout<<"CGlassBox析构函数执行......"<<endl; } private: }; //CCan类-它表示啤酒或可乐罐 //ectern const double PI; class CCan: public CContainer { public: virtual double Volume() const { return 0.25*PI*m_Diameter*m_Height; } CCan(double hv=4.0, double dv=2.0):m_Height(hv), m_Diameter(dv) { } ~CCan() { cout<<"CCan析构函数执行......"<<endl; } private: double m_Height; double m_Diameter; }; int main(array<System::String ^> ^args) { // CContainer* pContainer = new CGlassBox(2.0, 3.0, 4.0); //定义一个基类指针 pContainer->ShowVolume(); CBox* pBox = dynamic_cast<CBox*>(pContainer); //将基类指针强制转换为类层次结构中的CBox*类型 pBox->ShowVolume(); CGlassBox* pGlassBox = dynamic_cast<CGlassBox*>(pContainer); //转换为实际的类型CGlassBox* pGlassBox->ShowVolume(); //由于派生类对像是动态创建的,因此我们不需要它们时必须使用delete运算符清理自由存储器 system("pause"); return 0; }*/ //9.8 嵌套类 //嵌套类可以自由访问封装类的所有静态成员,通过封装类的对像或指向封装类对像的指针或引用,还可以访问所有实例成员 //封装类只能访问嵌套类的公有成员,但在封类所有私有的嵌套类中,类成员通常被声明为public,以使封装类的函数能够自由访问整个嵌套类 /* #include "stdafx.h" #include <iostream> using namespace std; using namespace System; const double PI = 3.14159265; //定义纯虚函数 class CContainer { public: virtual double Volume() const = 0; //定义虚函数Volume()的语句通过在函数头中添加等号和0,将该函数定义成没有任何内容,这样的函数被称为纯虚函数 virtual void ShowVolume() const { cout<<endl <<"Volume is"<<Volume()<<endl; } virtual ~CContainer(){ cout<<"CContainer 析构函数执行....."<<endl; } //一个问题:可以将构造函数声明为virtual吗?答案是不行的,只有析构孙函数和其它成员函数才可以 //当使用继承时,总是照例将基类的析构函数声明为虚函数是个好主意, //在为析构函数的执行过程中有少量的系统开稍,但大多数情况下不用理会这一点,使用虚析构函数能够确保正确的销毁对像, //还能避免相反情形下可能出现的程序崩溃风险 }; class CBox : public CContainer { public: virtual void ShowVolume() const { cout<<endl <<"CBox usable volume is "<<Volume(); } virtual double Volume() const { return m_Width * m_Height * m_Length; } CBox(double lv=1.0, double wv=1.0, double hv=1.0):m_Length(lv), m_Width(wv), m_Height(hv) { } ~CBox(){ cout<<"CBox析构函数执行......"<<endl; } protected: double m_Width; double m_Height; double m_Length; }; class CGlassBox : public CBox { public: virtual double Volume() const { return 0.85 * m_Width * m_Height * m_Length; } CGlassBox(double lv, double wv, double hv):CBox(lv, wv, hv) { } ~CGlassBox() { cout<<"CGlassBox析构函数执行......"<<endl; } private: }; //CCan类-它表示啤酒或可乐罐 //ectern const double PI; class CCan: public CContainer { public: virtual double Volume() const { return 0.25*PI*m_Diameter*m_Height; } CCan(double hv=4.0, double dv=2.0):m_Height(hv), m_Diameter(dv) { } ~CCan() { cout<<"CCan析构函数执行......"<<endl; } private: double m_Height; double m_Diameter; }; class CStack { private: struct CItem { CBox* pBox; //一个pBox指针 CItem* pNext; //一个CItem的指针,该指针的一下个指针 CItem(CBox* pB, CItem* pN):pBox(pB), pNext(pN) { } }; CItem* pTop; //定义头指针 public: void Push(CBox* pBox) { //生成一个CItem对像,传入pBox与当前的pTop指针,也就是当前的pTop指针为新的pTop指针的一下个指针 pTop = new CItem(pBox, pTop); } CBox* Pop() { if(pTop == 0){ return 0; } CBox* pBox = pTop->pBox; //取得当前头指针所指向的pBox对像,放入到pBox中去 CItem * pTemp = pTop; //将当前的头指针记录下来,放到pTemp中 pTop = pTop->pNext; //将头指针向下移动一位 delete pTemp; //删除记录的老的头指针 return pBox; //返回pBox对像 } }; int main(array<System::String ^> ^args) { CBox* pBoxes[] = { new CBox(2.0, 3.0, 4.0), new CGlassBox(2.0,3.0,4.0), new CBox(4.0,5.0,6.0), new CGlassBox(4.0,5.0,6.0)}; for(int i=0; i<4; i++){ pBoxes[i]->ShowVolume(); } cout<<endl<<"现在开始将四个对像全部装入Stack中"<<endl; CStack* pStack = new CStack; for(int i=0; i<4; i++){ pStack->Push(pBoxes[i]); } cout<<"现在开始读取Stack中的数据"<<endl; for(int i=0; i<4; i++){ pStack->Pop()->ShowVolume(); } //使用嵌套的Struct来定义堆栈中存储的对像,CStack类的确实现了一个堆栈 system("pause"); return 0; }*/ //9.9 C++/CLI编程 //包括用户定义的类在内的所有C++/CLI类,默认情况下都是派生类,因此数值和引用类都是以System::Object这个标准作为基类的 //因为System::Object是所有C++/CLI类的基类,所以句柄类型system::Object^起到了与本地C++中可用来引用任何类型的对像的void*类型相同的作用 //9.9.1 装箱与拆箱 //供所有数值类型使用的System::Object基类还负责实现基本类型数值的装箱(boxing)和拆箱(unboxing)操作 //装箱是将它转换为垃圾回收堆上的一个对像,因此它会与基类数值一起承载完整的类型信息 //拆箱是装箱的逆向操作 //double value = 3.14159265; //Object^ boxedValue = value; //第二个语句强制装箱value,装箱后的表示由句柄boxedValue引用 //long^ number = gcnew(999999L); //我们可以用解引用运算符对数值类型执行拆箱操作 //Console::WriteLine(*number); /*#include "stdafx.h" #include <iostream> using namespace std; using namespace System; int main(array<System::String ^> ^args) { double value = 3.14159265; Object^ boxedValue = value; Console::WriteLine(boxedValue); system("pause"); return 0; } */ //9.9.2 C++/CLI类的继承 //String^ ToString(); //返回对像的String表示法,System::Object类中的实现返回字符串形式的类名,我们通常要在自己的类中重新该函数,以返回对像值的字符串表示 //bool Equals(Object^ obj) //比较当前对像与obj,如果二者相等则返回true,否则返回false,这里的"相等"意味着引用相等性 //即两个对像是同一个,我们通常要在自己的类中重写该函数,以便宜当前对像与实参的值相等时,即二者的字符相等时返回true //int GetHashCode() //返回当前对像的散列码一个整数,在存储(key, object)对的集合中,散列码用于存储对象的键码,随后 //我们通过提供某个对像被存储时使用的键码,就可以从这样的集合中将该对像检索出来 /*#include "stdafx.h" #include <iostream> using namespace std; using namespace System; //abstrcat定义抽像类 ref class Container abstract { public: virtual double Volume() abstract; //类名后面的abstract关键字,如果某个C++/CLI类包含等价于本地C++类中纯虚函数的函数 //那么我们必须将该类指定为abstract virtual void ShowVolume() { Console::WriteLine(L"Volume is {0}", Volume()); } }; ref class Box: Container { public: virtual void ShowVolume() override { Console::WriteLine(L"Box usable volume is {0}", Volume()); } virtual double Volume() override { return m_Length * m_Width * m_Height; } //Box类重写了从基类继承的Volume()函数,当需要重写基类中的某个函数,我们必须总是指定override关键字 //如果Box类不实现Volume()函数,那么它将是一个抽像类,而为了成功编译这个类,则需要将其指定为abstruct Box():m_Length(1.0), m_Width(1.0), m_Height(1.0){} Box(double lv, double wv, double hv):m_Length(lv), m_Width(wv), m_Height(hv){} private: protected: double m_Length; double m_Width; double m_Height; }; //引用类的基类总是公有的,默认情况下编译器认为是有public关键字的 //不能像本地C++版本中那样给形参提供默认值 ref class GlassBox: Box { public: virtual double Volume() override { return 0.85 * m_Length * m_Width * m_Height; } GlassBox(double lv, double wv, double hv):Box(lv, wv, hv){} }; ref class Stack { private: ref struct Item { Object^ Obj; Item^ Next; Item(Object^ obj, Item^ next):Obj(obj), Next(next){} }; Item^ Top; public: void Push(Object^ obj) { Top = gcnew Item(obj, Top); } Object^ Pop() { if(Top == nullptr){ return nullptr; } Object^ obj = Top->Obj; Top = Top->Next; return obj; } //需要注意的第一点是区别是函娄的形参和字段现在的都是句柄,因为我们是在处理引用类对像 //内部的结构Item现在存储着Object^类型的句柄,这样的句柄允许在堆栈中存储任何CLR类类型的对像 //这意味着无论是数值类还是引用类都能被压入堆栈 //这一点对本地C++中CStack类的重大改进,我们不必担心用Pop()函数时删除Item对像的问题 }; //总结这些类同本地C++类的区别 //1 只有引用类可以是派生类 //2 派生引用类的基类始终都是public //3 引用类中没有定义的函数是抽像函数,必须使用关键字abstract声明 //4 我们必须通过在类名后面放上abstract关键字,将包含一个或多个抽像类显式指定为抽像类 //5 不包含抽像函数类也可以被指定为abstract,这种情况下我们将不能定义该类的实例 //6 当指定某个重定基类函数的函数时,我们必须显式使用override关键字 int main(array<System::String ^> ^args) { array<Box^>^ boxes = {gcnew Box(2.0, 3.0, 4.0), gcnew GlassBox(2.0,3.0,4.0), gcnew Box(5.0,6.0,7.0), gcnew GlassBox(5.0,6.0,7.0)}; Console::WriteLine(L"开始显示boxes的值:"); for each(Box^ box in boxes){ box->ShowVolume(); } Console::WriteLine(L"现在开始入栈操作:"); Stack^ stack = gcnew Stack; for(int i=0; i<4; i++){ stack->Push(boxes[i]); } cout<<"现在开始出栈操作:"; System::Object^ item; while((item = stack->Pop()) != nullptr){ safe_cast<Container^>(item)->ShowVolume(); } //重新定义stack值 for(int i=2; i<=12; i+=2) { Console::Write(L"{0,5}", i); stack->Push(i); } Console::WriteLine(); while((item=stack->Pop()) != nullptr){ Console::Write(L"{0,5}", item); } Console::WriteLine(); system("pause"); return 0; } */ //9.9.3 接口类 //接口类的定久看起来非常类似于引用类的定义,但是它是个完全不同的概念,接口类指定一级将由其它类实现的函数 //以提供标准化的,可提供某种体功能的方,数值类和引用类都可以实现接口,接口不定义任何自有的函数成员 //其指定的函数是由实现该接口的各个类来定义的 //我们可以像下面这样使上一个示例中的Box类实现System::IComparable接口 /*ref class Box : Container, IComparable { //接口名是在类类名Container后面,如果没有基类,则这里单独出现接口名 //引用类只有一个基类,但可以实现任意多的接口 //这样的类必须定义宣称要实现的各个接口指定的每一个函数 // public: virtual int COmpareTo(Object^ obj) { if(Volume() < safe_cast<Box^>(obj)->Volume()); return -1; else if(Volume() > safe_cast<Box^>(obj)->Volume()); return 1; else return 0; } }*/ //9.9.4 定义接口类 //我们使用关键字interface class 或者interface struct定义接口类,无论是使用interface clas还是interface struct来定义接口 //接口的所有成员默认都是公有的,我们不能将它们指定为其它类型,接口的成员可以是函数,包括运算符函数,属性,静态字段和事件 //interface class IController: ITelevison, IRecorder //{ //Members of IController //} //该接口继承了ITelevison IRecorder两个接口 //Box类必须实现IContainer接口类的两个函数成员,不然它就是一个抽像类,需要用abstract关键字来定义 //Box类中这两个孙函数那么 的定义没有附加override关键字,因为我们不是重定现有的函数,而是初次实现Volume()和ShowVolume()函数 #include "stdafx.h" #include <iostream> using namespace std; using namespace System; interface class IContainer { double Volume(); void ShowVolume(); //两个函数实际上是抽像函数,因为接口永远不包括函数的定义,当然,我们可以给这两个函数添加abstruct关键字 //但这样做是不必要的,接口定义中的实例数可以指定为virtual和abstract但这也不是必需的 }; //C++/CLI中的接口名以I打头,这个约定 ref class Box: IContainer { public: virtual void ShowVolume() { Console::WriteLine(L"CBox usable volume is {0}", Volume()); } virtual double Volume() { return m_Length * m_Width * m_Height; } Box() : m_Length(1.0), m_Width(1.0), m_Height(1.0){} Box(double lv, double wv, double hv):m_Length(hv), m_Width(wv), m_Height(hv) { } protected: double m_Length; double m_Width; double m_Height; }; //引用类的基类总是公有的,默认情况下编译器认为是有public关键字的 //不能像本地C++版本中那样给形参提供默认值 ref class GlassBox: Box { public: virtual double Volume() override { return 0.85 * m_Length * m_Width * m_Height; } GlassBox(double lv, double wv, double hv):Box(lv, wv, hv){} }; ref class Stack { private: ref struct Item { Object^ Obj; Item^ Next; Item(Object^ obj, Item^ next):Obj(obj), Next(next){} }; Item^ Top; public: void Push(Object^ obj) { Top = gcnew Item(obj, Top); } Object^ Pop() { if(Top == nullptr){ return nullptr; } Object^ obj = Top->Obj; Top = Top->Next; return obj; } }; int main(array<System::String ^> ^args) { array<IContainer^>^ containers = {gcnew Box(2.0, 3.0, 4.0), gcnew GlassBox(2.0,3.0,4.0), gcnew Box(5.0,6.0,7.0), gcnew GlassBox(5.0,6.0,7.0)}; Console::WriteLine(L"开始显示boxes的值:"); for each(IContainer^ container in containers){ container->ShowVolume(); } Console::WriteLine(L"现在开始入栈操作:"); Stack^ stack = gcnew Stack; for each(IContainer^ container in containers){ stack->Push(container); } cout<<"现在开始出栈操作:"; System::Object^ item; while((item = stack->Pop()) != nullptr){ safe_cast<IContainer^>(item)->ShowVolume(); } system("pause"); return 0; }