• Visual C++ 2008入门经典 第九章类的继承和虚函数(二) 简单


    ////////////////////////////////////////////////////////////////////////////////
    //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;
    }
    

      

  • 相关阅读:
    二分搜索树的深度优先遍历和广度优先遍历
    数据结构与算法之非比较排序【Java】
    数据结构与算法之比较排序【Java】
    像素 转换 px dp
    Toast
    MySQL丶auto_increment
    MariaDB · 性能优化 · Extended Keys
    加唯一索引怎么会导致丢数据
    语句执行错误一· Count(Distinct) ERROR
    innodb参数 &#183; innodb_flush_log_at_trx_commit
  • 原文地址:https://www.cnblogs.com/xiangxiaodong/p/2757995.html
Copyright © 2020-2023  润新知