C++学习之第十二天-虚函数、纯虚函数

知识点总结

1.动态多态被激活的五个条件--重要

1.基类定义虚函数
2.派生类重写该虚函数
3.创建派生类对象
4.基类的指针指向或者引用绑定到派生类的对象
5.基类用指针或者引用来调用虚函数

2.对虚函数进行访问的五种方式

1.通过指针来访问
    >使用指针访问虚函数时,编译器根据指针所指对象的类型决定要调用哪个函数(动态联编),而与指针本身的类型无关。
    
2.使用引用访问虚函数
    >与使用指针访问虚函数类似,表现出动态多态特性。
    >不同的是,引用一经声明后,引用变量本身无论如何改变,其调用的函数就不会再改变,始终指向其开始定义时的函数。
    >因此在使用上有一定限制,但这在一定程度上提高了代码的安全性,特别体现在函数参数传递等场合中,可以将引用理解成一种“受限制的指针”。
    
3.用对象访问虚函数---静态联编
    >和普通函数一样,虚函数一样可以通过对象名来调用,此时编译器采用的是静态联编。
    >通过对象名访问虚函数时, 调用哪个类的函数取决于定义对象名的类型。
     对象类型是基类时,就调用基类的函数;对象类型是子类时,就调用子类的函数。
    >静态联编-早绑定机制:
        函数重载、运算符重载都属于静态多态,C++编译器根据传递给函数的参数和函数名来决定具体要调用哪个函      数,已经被确定好函数的调用地址。
    >动态联编---晚绑定机制:late binding 
        程序在运行阶段才确定去调用哪个函数,在运行的时候才去确定函数的调用地址,C++通过虚函数来实现动态联编。


4.通过成员函数来访问虚函数-在普通成员函数里面调用虚函数
    可以在需要重载的虚函数后面加个overide,告诉编译器我要重载的对象是虚函数。
    
5.在构造函数和析构函数中访问虚函数----静态联编

    构造函数和析构函数是特殊的成员函数,在其中访问虚函数时,C++采用静态联编,即在构造函数或析构函数内,即使是使用“this->虚函数名”的形式来调用,编译器仍将其解释为静态联编的“本类名::虚函数名”。

    即它们所调用的虚函数是自己类中定义的函数,如果在自己的类中没有实现该函数,则调用的是基类中的虚函数。但绝不会调用任何在派生类中重定义的虚函数。 
 

3.纯虚函数

1.如果派生类不重写基类的所有纯虚函数,那么派生类也是个抽象类

2.有纯虚函数的类或者构造函数在protected权限下的类叫抽象类,抽象类不允许实例化对象,但能够创建指针或者引用来接收派生类的指针,实现动态多态。

3.抽象类只是对接口进行了声明,其实现是要交给派生类。抽象层---->实现层。

4.接口的定义:将内部的实现细节封装起来了,外部用户通过预留的接口来使用接口的功能。

4.纯虚函数的使用场景

1.抽象层是连接起实现层和业务层的桥梁,面向对象的设计原则是开闭原则:对扩展开发,对修改关闭。

2.抽象层作为接口,只作声明,实现由派生类的实现层来完成。

使用场景例子

#include <iostream>
using namespace std;
//接口层,抽象层
class Figure
{
public:
    virtual void display()const =0;

    virtual double area()const =0;
};
//实现层
class Rectangle:public Figure
{
public:
    Rectangle(double length=0, double width=0)
    :_length(length),
    _width(width)
    {
        cout<<"Rectangle(double,double)"<<endl;
    }
    virtual void display()const override
    {
        cout<<"length:"<<_length<<endl
            <<"width:"<<_width<<endl
            <<"area:"<<area()<<endl;
    }

    virtual double area()const override
    {
        return _length*_width;
    }

    ~Rectangle()
    {
        cout<<"~Rectangle()"<<endl;
    }
    double _length;
    double _width;
};
//实现层
class Circle:public Figure
{
public:
    Circle(double radius=0)
    :_radius(radius)
    {
        cout<<"Circle(double)"<<endl;
    }
    virtual void display()const override
    {
        cout<<"radius:"<<_radius<<endl
            <<"girth:"<<area()<<endl;
    }

    virtual double area()const override
    {
        return 2*_radius*3.14;
    }

    ~Circle()
    {
        cout<<"~Circle()"<<endl;
    }
    double _radius;
};

//业务层
void printArea(const Figure *area)//常对象只能去调用常函数,所以虚函数后都加上const override
{
    area->display();
}
void test01()
{
    Rectangle r1(10,40);
    Circle c1(7);
    printArea(&r1);
    cout<<endl<<endl;
    printArea(&c1);
}
int main()
{
    test01();
    return 0;
}

运行结果

Rectangle(double,double)
Circle(double)
length:10
width:40
area:400


radius:7
girth:43.96
~Circle()
~Rectangle()

选择题

1、设置虚基类的目的是(B)

A.简化程序 B.消除二义性 C.提高程序运行效率 D.减少目标代码

2、多继承派生类构造函数构造对象时,(B)被最先调用。

A.派生类自己的构造函数 B.虚基类的构造函数

C.非虚基类的构造函数   D.派生类中子对象类的构造函数

程序题

#include <iostream> 

using std::endl;
using std::cout;

class Base {
public:
    Base()
    {   
		a=5;
        cout<<"Base a="<<a<<endl;
    }  
protected:
    int a;
};

class  Base1
:virtual public Base
{
public:
    Base1()
    {	
		a = a + 10; 
	    cout<<"Base1 a = " << a <<endl;
	} 
};


class Base2
: virtual public Base
{
public:
    Base2()
    {
	   a = a + 20; 
	   cout << "Base2 a = " << a << endl;
    } 
};

class Derived
:public Base1
,public Base2
{
public:
    Derived()
    { 
	   cout<<"Derived a="<<a<<endl;
    } 
};

int main(void)
{ 
   Derived  obj;
   return  0; 
}

结果:

Base a=5
Base1 a = 15
Base2 a = 35
Derived a=35

编程题

1、编写一个抽象类Figure,该类拥有:

① 1个成员变量,存放图形的名字(是否该设计成private/protected?)

② 2个纯虚函数

virtual double getArea( )=0
virtual string getName( )=0
virtual void show()=0			//打印图形的相关信息

2、编写一个圆类Circle,让其继承自Figure类,该类拥有:

① 1个成员变量,存放圆的半径;(是否该设计成private/protected?)
② 2个构造方法
 	Circle( )              // 将半径设为0           
 	Circle(double  r )     //创建Circle对象时将半径初始化为r      
③ 3个成员方法           
	double getRadius( )   		//获取圆的半径
	double getPerimeter( )      //获取圆的周长
	virtual double getArea( )   //获取圆的面积
	virtual string getName( )   //获取圆的名字           

	void  show( )               //将圆的半径、周长、面积输出到屏幕 

3、编写一个圆柱体类Cylinder,它继承于上面的Circle类,还拥有:

① 1个成员变量,圆柱体的高;

② 构造方法

Cylinder (double r, double  h) //创建Circle对象时将半径初始化为r         
③ 成员方法
覆盖Circle的getArea( )   //获取圆柱体的表面积 
覆盖Circle的getName( )   //获取图形的名字
double getHeight( )      //获取圆柱体的高
double getVolume( )      //获取圆柱体的体积           
void  show()      		 //将圆柱体的高、表面积、体积输出到屏幕

4、编写测试用例,在实现的过程中,体会动态多态的用法。

① 创建类的对象,分别设置圆的半径、圆柱体的高

② 计算并分别显示圆半径、圆面积、圆周长,

③ 计算并分别显式圆柱体的高、表面积、体积。

参考:

#include <iostream>
using namespace std;
class Figure
{
public:
    Figure(string Name)
        :Figure_Name(Name)
    {

        cout<<"~Figure(string)"<<endl;
    }
    virtual double getArea()=0;//
    virtual string getName()=0;
    virtual void show()=0;

protected:
    string Figure_Name;
};
class Circle: public Figure
{
public:
    Circle(double r = 0, string Name = "Circle")
        :_radius(r),
        Figure(Name)
        {
            cout<<"Circle(double,string)"<<endl;

        }

    double getRadius()//获取半径
    {
        return _radius;
    }
    double getPerimeter()//获取周长
    {
        return 2*_radius*3.14;
    }
    virtual double getArea()//获取面积
    {
        return _radius*_radius*3.14;
    }
    virtual string getName()
    {
        return Figure_Name;
    }

    void show()override
    {
        cout<<"Name:"<<getName()<<endl;
        cout<<"Radius:"<<_radius<<endl
            <<"Area:"<<getArea()<<endl
            <<"Perimeter:"<<getPerimeter()<<endl;
    }

protected:
    double _radius;//半径
};
class Cylinder:public Circle//继承圆类
{
public:
    Cylinder(double r,double h,string Name = "Cylinder")
        :_h(h)
        ,Circle(r,Name)
        {
            cout<<"Cylinder(string, double,double)"<<endl;
        }
    double getArea()//面积
    {
        return 2*_radius*3.14*2+2*3.14*_radius*_h;
    }
    string getName()
    {
        return Figure_Name;
    }
    double getHeight()
    {
        return _h;
    }
    double getVolume()//体积
    {
        return _radius*3.14*_radius*_h;
    }
    void show()override
    {
        cout<<"Name:"<<getName()<<endl;
        cout<<"height:"<<_h<<endl;
        cout<<"Area:"<<getArea()<<endl
            <<"Volumn:"<<getVolume()<<endl;
    }

private:
    double _h;
};

void func(Figure &f1)//体现多态,业务层
{
    f1.show();
}
void test01()
{
    Cylinder cylinder1(10,10);//实现层
    Circle circle1(10);
    
    func(cylinder1);
    cout<<endl<<endl;
    func(circle1);
}
int main()
{
    test01();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/121278508