(作者有话说:文章包含的代码运行结果可能与图片结果有所差异,请根据自己的需要进行修改)
实验名称:类和对象的定义及使用
一、实验目的和要求
(1)掌握单继承和多重继承下派生类的定义方法,理解基类成员在不同的继承方式下不同的访问属性。
(2)正确定义派生类的构造函数与析构函数,理解定义一个派生类对象时各个构造函数、析构函数被调用的顺序。
(3)正确定义虚基类,消除在多层次多重继承方式下顶层基类中成员访问的二义性问题,关注此时各构造函数、析构函数的调用顺序。
(4)通过基类与公有派生类的定义,及基类对象、指针、引用与派生类的对象、地址间相互赋值的方法,正确理解赋值兼容的4种情况,通过程序理解其不可逆性。
二、实验环境(实验设备)
硬件: 微型计算机
软件: Windows 操作系统、Microsoft Visual Studio 2010
三、实验原理及内容
实验题目1: 定义一个车基类,派生出自行车类和汽车类,又以自行车类和汽车类为基类共同派生出摩托车类,每个类都要定义带有参数的构造函数。对自行车类继承车基类的方式分别用private、protected、public,观察基类成员在派生类中的访问属性;观察自行车类、汽车类和摩托车类对象定义时构造、析构函数的调用顺序。最后将车基类定义为虚基类再观察程序运行结果。
实验解答:
① 根据提示进行填写完整实验指导204页代码对应位置内容如下:
( 1 ) MaxSpeed = m;
( 2 ) Weight = w;
( 3 ) Vehicle(m, w)
( 4 ) Height = h;
( 5 ) Vehicle::Show();
( 6 ) cout << "It\'s height is:" << Height << endl;
( 7 ) b(15, 75, 60);
此时程序的运行结果是:
② 将继承方式改为private或protected,观察并分析程序的编译结果。
继承方式改为protected:
继承方式改为private:
③ 在Bicycle类下面增加Car类的定义,参考实验教材给出的代码,划线部分自己完成。
( 8 ) Vehicle(m, w)
( 9 ) SeatNum = s;
( 10 ) Vehicle::Show();
( 11 ) cout << "It\'s SeatNum is:" << SeatNum << endl;
( 12 ) b(15, 75, 60);
( 13 ) c(120, 1500, 100);
④ 增加的第3层类MotorCycle及修改以后的main( )函数,代码参见实验教材。
( 14 ) public Bicycle, public Car
( 15 ) mc(60, 240, 80, 2);
⑤ 将Vehicle声明为虚基类以消除二义性,具体要在上面的基础上修改3个地方。
· 将class Bicycle: public Vehicle 修改为 class Bicycle: virtual public Vehicle。
· 将class Car: public Vehicle 修改为 class Car: virtual public Vehicle。
· 在第3层类的构造函数MotorCycle(int m,int w,int h,int s): (16) 的初始化列表中增加对虚基类构造函数的调用。
( 16 ) Bicycle(m,w,h),Car(m,w,s),Vehicle(m,w)
实验题目1代码:
#include<iostream>
using namespace std;
class Vehicle
{
protected:
int MaxSpeed;
int Weight;
public:
Vehicle(int m, int w)
{
MaxSpeed = m;
Weight = w;
cout << "Constructing Vehicle…\n";
}
~Vehicle()
{
cout << "Destructing Vehicle…\n";
}
void Run()
{
cout << "The vehicle is running!\n";
}
void Stop()
{
cout << "Please stop running!\n";
}
void Show()
{
cout << "It\'s maxspeed is:" << MaxSpeed << endl;
cout << "It\'s weight is:" << Weight << endl;
}
};
class Bicycle :virtual public Vehicle
{
protected:
int Height;
public:
Bicycle(int m, int w, int h) :Vehicle(m, w)
{
Height = h;
cout << "Constructing Bicycle…\n";
}
~Bicycle()
{
cout << "Destructing Bicycle…\n";
}
void Show()
{
Vehicle::Show();
cout << "It\'s height is:" << Height << endl;
}
};
class Car :virtual public Vehicle
{
protected:
int SeatNum;
public:
Car(int m, int w, int s) :Vehicle(m, w)
{
SeatNum = s;
cout << "Constructing Car…\n";
}
~Car()
{
cout << "Destructing Car…\n";
}
void Show()
{
Vehicle::Show();
cout << "It\'s SeatNum is:" << SeatNum << endl;
}
};
class MotorCycle :public Bicycle, public Car
{
public:
MotorCycle(int m, int w, int h, int s) :Bicycle(m,w,h),Car(m,w,s),Vehicle(m,w)
{
cout<< "Constructing MotorCycle…\n";
}
~MotorCycle()
{
cout << "Destructing MotorCycle…\n";
}
void Show()
{
cout << "It\'s maxspeed is:" << MaxSpeed << endl;
cout << "It\'s weight is:" << Weight << endl;
cout << "It\'s height is:" << Height << endl;
cout << "It\'s SeatNum is:" << SeatNum << endl;
}
};
int main()
{
Bicycle b(15, 75, 60);
b.Run();
b.Stop();
b.Show();
Car c(120, 1500, 5);
c.Run();
c.Stop();
c.Show();
MotorCycle mc(60, 240, 80, 2);
mc.Run();
mc.Stop();
mc.Show();
return 0;
}
实验题目2:定义Base类及它的公有派生类Derived类,两个类中均定义带参数的构造函数,基类中定义函数Show( ),派生类中也定义一个同名的Show( ),二者输出内容有所区别。主函数中定义基类的对象、指针、引用,也定义派生类的对象。
① 对赋值兼容的4种情况作测试,对每行的输出结果进行观察,理解赋值兼容何时调用基类的成员函数,什么情况下才会调用派生类的成员函数。
② 在主函数的return 0;语句前增加4条语句,观察并记下编译时的报错信息,理解赋值兼容的不可逆性。
实验解答:
①按提示将程序填写完整,代码参见实验教材,对应位置内容是:
(1 ) b1(2);
(2 ) d1(5);
(3 ) b1 = d1;
(4 ) & b2 = d1;
(5 ) * b3 = &d1;
(6 ) new Derived(6);
程序的运行结果是:
② 在主函数的return 0;语句前增加4条语句:
Derived d5=b1;
Derived &d6=b1;
Derived *d7=&b1;
d7=b3;
观察并记下编译时的报错信息,理解赋值兼容的不可逆性。
“初始化”: 无法从“Base”转换为“Derived”
“初始化”: 无法从“Base”转换为“Derived &”
“初始化”: 无法从“Base *”转换为“Derived *”
“=”: 无法从“Base *”转换为“Derived *”
实验题目2代码:
#include<iostream>
using namespace std;
class Base
{
public:
int i;
Base(int x) :i(x)
{
cout << "Constructing base!" << endl;
}
void show()
{
cout << "i in Base is:" << i << endl;
}
};
class Derived :public Base
{
public:
Derived(int x) :Base(x)
{
cout << "Constructing derived!" << endl;
}
void show()
{
cout << "i in Derived is:" << i << endl;
}
};
int main()
{
Base b1(2);
cout << "基类对象 b1.show( ):\n";
b1.show();
Derived d1(5);
b1 = d1;
cout << "基类b1=d1, b1.show( ):\n";
b1.show();
cout << "派生类对象 d1.show( ):\n";
d1.show();
Base& b2 = d1;
cout << "引用 b2=d1,b2.show( ):\n";
b2.show();
Base* b3 = &d1;
cout << "基类指针 b3=&d1,b3->show( ):\n";
b3->show();
Derived* d4 = new Derived(6);
Base* b4 = d4;
cout << "基类指针 b4 = d4,b4->show( ):\n";
b4->show();
cout << "派生类指针 d4, d4->show( ):\n";
d4->show();
delete d4;
return 0;
}
四、实验小结(包括问题和解决方法、心得体会、意见与建议等)
(一)实验中遇到的主要问题及解决方法
1.在题目(1)中将Bicycle继承Vehicle类的方式分别修改为protected和private,再重新编译,请在小结中记录报错信息,解释原因。记录采取何种修改方式使程序正确运行?
报错信息:
“Vehicle::Run”不可访问,因为“Bicycle”使用“private”方式从“Vehicle”继承
“Vehicle::Stop”不可访问,因为“Bicycle”使用“private”方式从“Vehicle”继承
“Vehicle::Run”不可访问,因为“Bicycle”使用“protected”方式从“Vehicle”继承
“Vehicle::Stop”不可访问,因为“Bicycle”使用“protected”方式从“Vehicle”继承
原因:
采用“private”和“protected”继承,使得基类中可被继承的成员访问属性变为“private”或“protected”,而无法被访问
修改方式:
采取“public”继承方式可以使程序正确运行。
2. 在题目(2)中观察运行结果,总结在有赋值兼容的情况下,何时调用基类的成员函数,何时才会调用派生类的成员函数。
调用基类的成员函数:
(1)基类调用虚基类函数且对基类对象赋值
(2)调用非虚基类函数
调用派生类的成员函数:
调用虚基类函数且对基类的对象引用和基类的对象指针进行赋值
3.其它问题及解决方法:
问题:
定义派生类对象并生成新对象,忘记关键字new的用法,即给指针申请新空间
解决办法:
查找书籍,复习回顾new的用法
(二)实验心得
通过本次实验,我了解的继承的三种方式的区别以及调用基类构造函数和基类其他成员函数的方法,同时理解了赋值兼容的4种情况。