第8章 多态性
8-4 请编写一个计数器 Counter类,对其重载运算符“+”。
#include <iostream>
using namespace std;
class Counter{
private:
int cnt;
public:
Counter(int n){
cnt=n;
}
Counter():cnt(0){ }
Counter operator+(const Counter& t){
Counter r;
r.cnt=cnt+t.cnt;
return r;
}
void display(){
cout<<"counter = "<<cnt<<endl;
}
~Counter(){ }
};
int main()
{
Counter a(100), b(200),c;
c = a + b;
c.display();
return 0;
}
8-5 编写一个哺乳动物类 Mammal,再由此派生出狗类 Dog,二者都声明 speak()成员函数,该函数在基类中被声明为虚函数。声明一个 Dog 类的对象,通过此对象调用speak 函数,观察运行结果。
#include <iostream>
using namespace std;
class Mammal{
public:
Mammal(){cout << "Constructor in Mammal.\n";}
virtual ~Mammal(){cout << "Destructor in Mammal.\n";}
virtual void speak(){
cout<<"Mammal speak!\n";
}
};
class Dog:public Mammal{
public:
Dog(){cout << "Constructor in Dog.\n";}
~Dog(){cout << "Destructor in Dog.\n";}
void speak(){cout << "Dog sound wang,wang,wang!\n";}
};
int main()
{
Mammal *m = new Dog;
m->speak(); //使用虚函数,根据指针指向的类型执行哪种方法
delete m;
return 0;
}
8-6 请编写一个抽象类 Shape,在此基础上派生出类 Rectangle 和 Circle,二者都有计算对象面积的函数 getArea()、计算对象周长的函数 getPerim ()。
#include <iostream>
using namespace std;
class shape{
public:
shape(){}
~shape(){}
virtual double getarea()=0;
virtual double getperim()=0;
};
class rectangle:public shape{
public:
rectangle(double l,double w):pl(l),pw(w){}
~rectangle();
virtual double getarea(){return pl*pw;}
virtual double getperim(){return 2*(pl+pw);}
private:
double pl;
double pw;
};
class circle:public shape{
public:
circle(double r):pr(r){}
~circle();
virtual double getarea(){return 3.14*pr*pr;}
virtual double getperim(){return 2*3.14*pr;}
private:
double pr;
};
int main(){
shape *sp;
sp=new circle(1);
cout<<"圆的面积是:"<<sp->getarea()<<endl;
cout<<"圆的周长是:"<<sp->getperim()<<endl;
delete sp;
sp= new rectangle(10,85);
cout<<"矩形的面积是:"<<sp->getarea()<<endl;
cout<<"矩形的周长是:"<<sp->getperim()<<endl;
delete sp;
return 0;
}
8-7 对类 Point重载“++”(自增)、“–”(自减)运算符,要求同时重载前缀和后缀的形式。
#include <iostream>
using namespace std;
class Counter{
private:
int cnt;
public:
Counter(int n = 0){
cnt=n;
}
Counter& operator++(){ //后置单目运算符++
cnt++;
return *this;
}
Counter operator++(int){ //前置单目运算符++
Counter t = *this;
++(*this);
return t;
}
Counter& operator--(){ //后置单目运算符--
cnt--;
return *this;
}
Counter operator--(int){ //前置单目运算符--
Counter t = *this;
--(*this);
return t;
}
void display(){
cout<<"counter = "<<cnt<<endl;
}
};
int main() {
Counter a(100);
++++a;
a.display();
for( int i = 0; i < 100; i++ )
a++;
a.display();
----a;
a.display();
for( int i = 0; i < 100; i++ )
a--;
a.display();
return 0;
}
其中,前置单目运算符和后置运算符的重载最主要的区别就在于重载函数的形参。语法规定,前置单目运算符重载为成员函数时没有形参,而后置单目运算符重载为成员函数时需要有一个int型形参。
前置方式和后置方式的函数内部的语句可以相同,也可以不同,取决于编程的需要。
运算符重载
通常情况下:
1、单目运算符:
重载为成员函数的话,需要0个参数,重载为友元的时候需要一个参数
2、 双目运算符:
重载为成员函数的话,需要一个参数,重载为友元的时候需要俩个参数
friend Complex operator+(Complex &a, Complex &b);
//operator+(a,b);
const Complex operator+(Complex & another);
//a.operator+(b);
//成员的话 当中自己包含一个this 所以只需要一个参数
3、三目运算符没有重载
8-8 定义一个基类 BaseClass,从它派生出类 DerivedClass。BaseClass 有成员函数fn1(),fn2(),fn1()是虚函数;DerivedClass 也有成员函数 fn1(),fn2()。在主函数中声明一个 Derived C lass的对象,分别用 BaseClass 和 Derived C lass的指针指向DerivedClass的对象,并通过指针调用 fn1(),fn2(),观察运行结果。
#include<iostream>
using namespace std;
class BaseClass{
public:
virtual void fn1(){
cout<<"基类的虚函数 fn1()"<<endl;
}
void fn2(){
cout<<"基类的非虚函数 fn2()"<<endl;
}
//使用虚函数,会依据指针实际指向的对象类型来决定调用谁的方法,实现动态联编
//不使用虚函数,会根据指针本身的类型(或引用类型)来执行对应的方法,实现静态联编
};
class DerivedClass:public BaseClass{
public:
void fn1(){
cout<<"派生类的函数 fn1()"<<endl;
}
void fn2(){
cout<<"派生类的函数 fn2()"<<endl;
}
};
int main()
{
DerivedClass d;
d.fn1();
d.fn2();
BaseClass *p1 =&d;
p1->fn1();
p1->fn2();
DerivedClass *p2=&d;
p2->fn1();
p2->fn2();
return 0;
}
8-10 编写程序定义类 Point,有数据成员 x,y,为其定义友元函数实现重载“+”。
#include <iostream>
using namespace std;
class Point{
private:
int x,y;
public:
Point(){x=0,y=0;}
Point(int a,int b){
x=a;
y=b;
}
friend Point operator+(Point& c,Point& d);
void display(){
cout<<"("<<x<<","<<y<<")"<<endl;
}
};
Point operator+(Point& c,Point& d)
{
Point r;
r.x=c.x+d.x;
r.y=c.y+d.y;
return r; //注意返回的类型
}
int main() {
Point p1(1,2), p2(-1,1), p3;
p1.display();
p2.display();
p3 = p1 + p2;
cout<<"p1+p2=";//若写为<<p3.display(),则会出现错误提示,因为没有定义重载运算符<<
p3.display();
return 0;
}
8-11 在例 8-6 的基础上,通过继承 Rectangle 得到一个新的类 Square,然后在 Shape 中增加一个函数 int getVertexCount() const用来获得当前图形的顶点个数。用以下几种方法分别实现,并体会各自的优劣。
(3)将 Shape::getVertexCount声明为纯虚函数,在派生类中给出具体实现。
#include <iostream>
using namespace std;
class Shape{
public:
Shape(){}
~Shape(){}
virtual double getarea()=0;
virtual double getperim()=0;
virtual int getVertexCount()const=0;
};
class Circle:public Shape{
public:
Circle(double r):pr(r){}
~Circle();
double getarea(){return 3.14*pr*pr;}
double getperim(){return 2*3.14*pr;}
virtual int getVertexCount()const{
return 0;
}
private:
double pr;
};
class Rectangle:public Shape{
public:
Rectangle(double l,double w):pl(l),pw(w){}
~Rectangle();
virtual double getarea(){return pl*pw;}
virtual double getperim(){return 2*(pl+pw);}
virtual int getVertexCount()const{
return 4;
}
protected:
double pl;
double pw;
};
class Square:public Rectangle{
public:
Square(float len):Rectangle(len,len){}
~Square(){}
virtual double getarea(){return pl*pw;}
virtual double getperim(){return 2*(pl+pw);}
virtual int getVertexCount()const{
return 4;
}
};
int main(){
Shape *sp;
sp=new Circle(1);
cout<<"圆的面积是:"<<sp->getarea()<<endl;
cout<<"圆的周长是:"<<sp->getperim()<<endl;
cout<<"圆的顶点个数是:"<<sp->getVertexCount()<<endl;
cout<<endl;
delete sp;
sp= new Rectangle(10,85);
cout<<"矩形的面积是:"<<sp->getarea()<<endl;
cout<<"矩形的周长是:"<<sp->getperim()<<endl;
cout<<"长方形的顶点个数是:"<<sp->getVertexCount()<<endl;
cout<<endl;
delete sp;
sp= new Square(6);
cout<<"正方形的面积是:"<<sp->getarea()<<endl;
cout<<"正方形的周长是:"<<sp->getperim()<<endl;
cout<<"正方形的顶点个数是:"<<sp->getVertexCount()<<endl;
cout<<endl;
delete sp;
return 0;
}
- 其中,Shape类中成员函数getarea()、getperim()、getVertexCount()都声明为纯虚函数之后,基类中可以不再给出函数的实现部分,而纯虚函数的函数体由派生类给出。且Circle、Rectangle、Square类的这三个成员函数都声明为虚函数,让基类Shape类的指针sp可以调用new对象对应的、需要的功能函数,这时,派生类的虚函数会覆盖基类的虚函数,发生了动态绑定,实现多态效应。
- 此外,需要注意的是,Square类的几个成员函数都有访问到父类的数据pl,pw,因私有private成员只能被本类成员(类内)和友元访问,不能被派生类访问,而保护protected成员可以被派生类访问,故将pl,pw声明定义为保护成员,以供派生类Square类的成员函数使用。
总结 :
此习题章内容涉及到指针的多态效应(静态绑定、动态绑定)以及还有运算符重载等内容,可以参考以下网址进行学习理解:C++多态性实例讲解 - C++教程 - C语言网C++多态性实例讲解多态性多态性是面向对象程序设计的重要特性之一,从字面意思上可以简单理解就是:多种形态,多个样子。其实本质意思也是这样,在面向对象程序设计中,指同样的方法被不同对象执行时会有不同的执行效果。具体来说,多……https://www.dotcpp.com/course/79
本专栏为本人大二C++课程的习题作业和一些学习经验的分享,供大家参考学习。如有侵权请立即与我联系,我将及时处理。
参考书籍为:C++语言程序设计 第五版 -清华大学出版社- 郑莉,董渊、C++语言程序设计 第五版 -清华大学出版社- 郑莉,董渊(学生用书)
编译环境:Visual Studio 2019、Dev-C++
欢迎关注我的微信公众号,分享一些有趣的知识:程序猿的茶水间