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

  

posted @ 2012-11-07 00:03  简单--生活  阅读(381)  评论(0编辑  收藏  举报
简单--生活(CSDN)