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

类的多态

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

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

一、运算符重载

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;

}
···

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

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

扫描二维码关注公众号,回复: 7649168 查看本文章

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

(三)、纯虚函数与抽象

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

纯虚函数可以实现其功能

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、正确的运行结果:

猜你喜欢

转载自www.cnblogs.com/myself914/p/11748698.html