知识点总结
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;
}
|