一题目
请设计以下航行器、飞机、船、水上飞机等 4 个类。
-
CRAFT 为航行器类,是公共基类,提供航行器的基本特性。包括:
一个保护数据成员:speed(速度)。
三个公有成员函数:构造函数(初始化速度)、析构函数和 Show 函数(显示速度)。
-
PLANE 为飞机类,以公有方式继承 CRAFT 类,在航行器类的基础上增加飞机的特性。包括:
一个保护数据成员:width(翼展)。
三个公有成员函数:构造函数(初始化速度和翼展)、析构函数和 Show 函数(显示速度和翼展)。
-
SHIP 为船类,以公有方式继承 CRAFT 类,在航行器类的基础上增加船的特性。包括:
一个保护数据成员:depth(吃水深度)。
三个公有成员函数:构造函数(初始化速度和吃水深度)、析构函数和 Show 函数(显示速度和吃水深度)。
-
SEAPLANE 为水上飞机类,同时以公有方式继承 PLANE 类和 SHIP 类,兼具飞机和船的特性。包括:
三个公有成员函数:构造函数(初始化速度、翼展、吃水深度)、析构函数和 Show 函数(显示速度、翼展和吃水深度)。
-
测试用例
- 样例一
-
CRAFT *p; p = new CRAFT(87.2); p->Show(); delete p;
创建航行器(速度: 87.2) 航行(速度: 87.2) 销毁航行器(速度: 87.2)
- 样例二
-
CRAFT *p; p = new PLANE(613.5, 45.3); p->Show(); delete p;
创建航行器(速度: 613.5) 创建飞机(翼展: 45.3) 航行(速度: 613.5, 翼展: 45.3) 销毁飞机(翼展: 45.3) 销毁航行器(速度: 613.5)
- 样例三
-
CRAFT *p; p = new SHIP(45.8, 8.3); p->Show(); delete p;
创建航行器(速度: 45.8) 创建船(吃水: 8.3) 航行(速度: 45.8, 吃水: 8.3) 销毁船(吃水: 8.3) 销毁航行器(速度: 45.8)
- 样例四
-
CRAFT *p; p = new SEAPLANE(415.2, 36.5, 2.1); p->Show(); delete p;
创建航行器(速度: 415.2)
创建飞机(翼展: 36.5)
创建船(吃水: 2.1)
创建水上飞机
航行(速度: 415.2, 翼展: 36.5, 吃水: 2.1)
销毁水上飞机
销毁船(吃水: 2.1)
销毁飞机(翼展: 36.5)
销毁航行器(速度: 415.2)
二.代码部分
#include <iostream>
using namespace std;
class CRAFT
{
protected:
double speed;
public:
CRAFT(double s)
{
speed = s;
cout << "创建航行器(速度: " << speed << ")" << endl;
}
virtual void Show()
{
cout << "航行(速度: " << speed << ")" << endl;
}
virtual ~CRAFT()
{
cout << "销毁航行器(速度: " << speed << ")" << endl;
}
};
class PLANE :virtual public CRAFT
{
protected:
double width;
public:
PLANE(double s, double w) :CRAFT(s), width(w)//调用CRAFT的构造函数,传递speed
{
cout << "创建飞机(翼展: " << width << ")" << endl;
}
void Show()override
{
cout << "航行(速度: " << speed << ", 翼展: " << width << ")" << endl;
}
~PLANE()override
{
cout << "销毁飞机(翼展: " << width << ")" << endl;
}
};
class SHIP :virtual public CRAFT
{
protected:
double depth;
public:
SHIP(double s, double d) :CRAFT(s), depth(d)//调用CRAFT的构造函数,传递speed
{
cout << "创建船(吃水: " << depth << ")" << endl;
}
void Show()override
{
cout << "航行(速度: " << speed << ", 吃水: " << depth << ")" << endl;
}
~SHIP()override
{
cout << "销毁船(吃水: " << depth << ")" << endl;
}
};
class SEAPLANE : virtual public PLANE, virtual public SHIP
{
public:
SEAPLANE(double s, double w, double d) :CRAFT(s), PLANE(s, w), SHIP(s, d)//调用CRAFT的构造函数,传递speed,调用 PLANE 的构造函数,传递 speed 和 width参数,调用 SHIP 的构造函数,传递 speed 和 depth参数
{
cout << "创建水上飞机" << endl;
}
void Show()override
{
cout << "航行(速度: " << speed << ", 翼展: " << width << ", 吃水: " << depth << ")" << endl;
}
~SEAPLANE()override
{
cout << "销毁水上飞机" << endl;
}
};
int main()
{
double s, w, d;
CRAFT* p;//CRAFT类型的指针
cin >> s >> w >> d;
p = new SEAPLANE(s, w, d);//实际指向SEAPLANE类的对象(未命名)
p->Show();//调用SEAPLAENE的show函数
delete p;
return 0;
}
输出:
创建航行器(速度: 583.6)
创建飞机(翼展: 48.2)
创建船(吃水: 3.8)
创建水上飞机
航行(速度: 583.6, 翼展: 48.2, 吃水: 3.8)
销毁水上飞机
销毁船(吃水: 3.8)
销毁飞机(翼展: 48.2)
销毁航行器(速度: 583.6)
三总结
1.继承
方式
class 派生类:继承方式 基类
继承后的访问权限
记忆方式:假设
public>protected>private
取基类属性和继承方式中较小的作为在派生类的访问属性
1.基类的pirvate在派生类中无法访问
2.基类的protected继承后可以在派生类中访问类外不行
2.虚函数
虚函数允许在派生类中重写基类的函数,从而实现运行的多态性
核心目的是通过基类访问派生类定义的函数,以便在通过基类指针引用或调用的时候可以执行派生类中的版本
#include<iostream>
using namespace std;
class A//基类
{
public:
void foo()
{
cout << "1" << endl;
}
virtual void fun()//声明为虚函数virtual关键字
{
cout << "2" << endl;
}
};
class B :public A//派生类
{
public:
void foo()//隐藏:派生类的函数屏蔽了与其同名的基类函数
{
cout << "3" << endl;
}
void fun()//多态覆盖
{
cout << "4" << endl;
}
};
int main()
{
A a;
B b;
A* p = &a;//这里定义了一个指向 A 类的指针 p,并将其初始化为指向 a 对象的地址。
p->foo();//输出1
p->fun();//输出2
p = &b;//我们将指针 p 的指向从 a 对象更改为 b 对象。注意,此时 p 仍然是一个指向 A 的指针,但它现在指向了 B 类的对象 b。
p->foo();//取决于指针类型,定义为A类指针,故输出1
p->fun();//取决于对象类型,指向了B的b对象,输出4(虚函数调用B类的fun)
return 0;
}
总结
- 非虚函数(如
foo()
)在运行时不会被动态绑定。它们总是根据指针或引用的静态类型(即声明时的类型)来调用。 - 虚函数(如
fun()
)在运行时会被动态绑定。它们会根据指针或引用实际指向的对象的类型来调用。 - 仅在虚函数的顶层基类加上virtual关键字,子类不加,而改用override关键字
有了这方面的只是支持,我们现在来理解题目的输出部分:
1.构造函数不会被多态性影响。无论是通过基类指针还是派生类对象本身,构造函数总是会被调用,并且会调用基类的构造函数。
2.成员函数,当他们是虚函数时,他们被通过基类指针或调用引用时会表现出多态性,即会调用与指针或调用引用实际指向的对象类型相对应的函数版本。
3.析构函数,与构造函数类似,但与虚函数关系密切。如果基类的析构函数时虚函数时,则会表现出多态性,通过基类指针删除派生类对象时,会先调用派生类的析构函数,在调用基类的。如果基类的析构函数不是虚函数,那么只会调用基类的析构函数,这可能会导致派生类部分没有被正确清理,造成资源泄漏或其他问题。
3.虚继承
在C++中,虚继承是用于解决多重继承中公共基类(或称为“菱形继承”或“钻石问题”)的重复子对象问题的一种机制。当一个类(SEAPLANE)从多个类(SHIP,PLANE)继承,而这些类又都从一个公共基类(CRAFT)继承时,如果没有使用虚继承,那么该公共基类在派生类中将会有多个实例(或称为子对象),这通常不是我们想要的结果。
为了解决这个问题,C++引入了虚继承的概念。当某个基类被声明为虚继承时,它的所有派生类(无论是直接派生还是间接派生)都会共享这个基类的单一实例,而不是每个派生类都有自己的一个实例。