代码改变世界

c++第五次作业—类的多态

2019-10-27 21:21  myself914  阅读(234)  评论(0编辑  收藏  举报

类的多态

···重载多态、包含多态、强制多态和参数多态

···重点介绍重载多态中的
运算符重载和包含多态

一、运算符重载

1、运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同的类型数据时导致不同的行为。

2、重载形式:类的非静态成员函数和非成员函数(当以非成员函数形式时,有时需要访问运算符参数所涉及类的私有成员,这时可以把函数声明为类的友元函数)

3、语法形式:

返回类型 operator 运算符(形参表)
{
函数体
}

运算符重载为成员函数

···
#include<iostream>
using namespace std;
class Counter
{
public:
	Counter(int i=0)
	{
		count=i;
	}
	Counter operator+(const Counter &c) const;
	int display()
	{
		return count;
	}

private:
	int count;
};
Counter Counter::operator+(const Counter &c) const
{
	return Counter(count+c.count);
}
int main()
{
	Counter c1(9),c2(7),c3;
	cout<<"c1="<<c1.display()<<endl;
	cout<<"c2="<<c2.display()<<endl;
	c3=c1+c2;//c3=c1.operator-(c2)
	cout<<"c1+c2="<<c3.display()<<endl;
	return 0;
}
···

运行结果:

运算符重载为非成员函数

···
#include<iostream>
using namespace std;
class Complex
{
public:
	Complex(double r=0.0,double i=0.0)
	{
		real=r;
		imag=i;
	}
	friend Complex& operator++(Complex &c);
	friend Complex operator++(Complex &c,int);
	void display()
	{
		cout<<"("<<real<<","<<imag<<")"<<endl;
	}

private:
	double real;
	double imag;
};

Complex& operator++(Complex &c)
{
	c.imag++;
	c.real++;
	return c;
}
Complex operator++(Complex &c,int)
{
	Complex old=c;
	++c;
	return old;
}
int main()
{
	Complex c1(6,8),f,d;

	cout<<"first time output"<<endl;
	c1.display();
	
	cout<<"后置单目后:"<<endl;
	f=c1++;
	f.display();
	cout<<"前置单目后:"<<endl;
	d=++c1;
	d.display();
	return 0;

}


···

运行结果:

从上述两个实验可以看出这两种重载形式的区别:重载为类的成员函数时,第一个操作数会被作为函数调用目的对象,所以无须出现在参数列表中,函数的参数个数比原来的操作数个数要少一个;而重载为非成员函数时,运算符的所有操作数必须显示通过参数传递

二、包含多态

(一)、虚函数

问题:用基类类型的指针指向派生类对象,但是当用它访问该对象时,访问到的只是从基类继承的同名成员,怎样可以访问到派生类中的同名成员呢?
包含多态中的虚函数可以解决这一问题

···在基类中将这个同名函数说明为虚函数。

声明语法:virtual 函数类型 函数名(形参表)

举例如下:

···
#include<iostream>
using namespace std;
class BaseClass
{
public:
	virtual void fun1() const;
	void fun2()
	{
		cout<<"output BaseClass2"<<endl;
	}
};
void BaseClass::fun1() const
{
	cout<<"output BaseClass1"<<endl;
}
class DerivedClass:public BaseClass
{
public:
	void fun1() const
	{
		cout<<"output DerivedClass1"<<endl;
	}
	void fun2()
	{
		cout<<"output DerivedClass2"<<endl;
	}
};
int main()
{
	DerivedClass a;
	BaseClass *b;
	DerivedClass *c;
	b=&a;
	b->fun1();
	b->fun2();
	//b->BaseClass::fun1()
	c=&a;
	c->fun1();
	c->fun2();
	return 0;
}
···

1、对比使用虚函数的fun1和没有使用虚函数的fun2,结果是:

,可以看出基类指针*b在访问虚函数fun1时,访问的时派生类的同名函数,而访问fun2时访问的是从基类继承来的fun2函数。

2、如果想要指针仍然可以访问基类中被派生类覆盖的成员函数,可以使用“::”进行限定。 将上述例子中改为:

,结果为:

,可以看出加了限定后可以访问基类中被覆盖的虚函数。

(二)、虚析构函数

如果有可能通过基类指针调用对象的析构函数(通过delete),就需要让基类的析构函数成为虚函数,否则会产生不确定的后果,通过以下例子对比说明:

···

#include<iostream>
using namespace std;
class Base
{
public:
	virtual ~Base();
	//~Base();
	
};
Base::~Base()
{
	cout<<"Base destructor"<<endl;
}
class Derived:public Base
{
public:
	Derived();
	~Derived();
private:
	int *p;
	
};
Derived::Derived()
{
	p = new int(0);
}
Derived::~Derived()
{
	cout << "Derived destructor" << endl;
	delete p;
}
void fun(Base *b)
{
	delete b;
}
int main()
{
	Base *b = new Derived();
	fun(b);
	return 0;

}
···

未声明为虚析构函数时的结果:

声明为虚析构函数时的结果:

原因是:通过基类指针删除派生类对象时调用的是基类的析构函数,派生类的析构函数没有被执行,因此派生类对象中动态分配的内存空间没有得到释放,程序如果持续发生这样的错误很危险,避免错误的有效方法就是将析构函数声明为虚函数。****************

(三)、纯虚函数与抽象

问题:在基类中声明与派生类相同原型的函数,将它们作为虚函数,这样派生类中的这几个函数就是对基类相应函数的覆盖,通过基类指针调用时,派生类函数被调用,然而基类中的这些函数可不可以不给出实现,只是先在基类中说明函数原型,再在派生类中给出具体实现呢?

纯虚函数可以实现其功能
####1、带有纯虚函数的类是抽象类,抽象类不能实例化(不可以定义一个抽象类的对象,但是可以定义一个抽象类的指针和引用) ####2、抽象类派生出新的类后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以定义自己的对象,不再是抽象类。 抽象类举例:
···

#include<iostream>
using namespace std;
class Shape
{
public:
	virtual double getArea() const=0;
	virtual double getPerim() const=0;
};
class Rectangle:public Shape
{
public:
	Rectangle(double len=0.0,double wid=0.0)
	{
		length=len;
		width=wid;
	}
	double getArea() const
	{
		return length*width;
	}
    double getPerim() const
	{
		return 2*(length+width);
	}
private:
	double length;
	double width;
};
class Circle:public Shape
{
public:
	Circle(double r)
	{
		ridus=r;
	}
	double getArea() const
	{
		return 3.14*ridus*ridus;
	}
    double getPerim() const
	{
		return 2*3.14*ridus;
	}
private:
	double ridus;
};
void showArea(Shape *a)
{
	cout<<"the area is:"<<a->getArea()<<endl;
}
void showPerim(Shape *a)
{
	cout<<"the perim is:"<<a->getPerim()<<endl;
}
int main()
{
	//Shape a;
	Rectangle b(5.1,7.9);
	Circle c(3.2);
	showArea(&b);
	showPerim(&b);
	showArea(&c);
	showPerim(&c);
	return 0;




}
···

1、如果定义一个抽象类的对象:

,所以抽象类不能实例化
2、如果上述例子Rectangle不给出getArea()的实现:结果

,所以派生类不给出所有纯虚函数的实现的话,仍然是个抽象类,不能实例化。
3、正确的运行结果: