-
2021-06-22 22:18:46
面向对象程序设计基于数据抽象、继承、动态绑定这三个基本概念。
- OOP的核心思想:多态性(polymorphism)
- 继承(inheritance)
- 基类(base class)
- 直接基类(direct base)
- 间接基类(indirect base)
- 派生类(derived class)
- 虚函数(virtual function) 其解析过程发生在运行时,而不是编译时
- 类派生列表(class derivation list)
- 动态绑定(dynamic binding) 使用基类的引用或指针调用一个虚成员函数时会执行动态绑定,使用指针调运虚成员函数时,根据指针所指向的实际对象来 。 在运行时选择函数的版本
- 覆盖(override)
- 访问说明符(public, protected, private):控制派生类从基类继承而来的成员是否对派生类的用户可见
- 共有(public)继承时,才能把派生类型的对象绑定到基类的引用或指针上
- 在形参列表、const修饰符、引用限定符后面添加override关键字,来显示地注明它是覆盖了它继承的虚函数
- static 成员在继承体系中唯一存在
- 派生类的作用域嵌套在基类的作用域中
- final类名后添加final关键字,禁止进程该类
- 先构造基类再构造派生类,先析构派生类再析构基类
- 构造函数不能继承,C++11中可用
using Base::Base;
声明继承构造函数(生成相同的构造函数)- 该using声明不会改变构造函数原有的访问基本
- 默认实参不会被继承,而是获得多个继承的构造函数,每个构造函数省略掉几个有默认实参的形参
类型转换
- 静态类型(static type)
- 编译时已知的,指针/引用对应的类型
- 决定那些成员是可见的
- 动态类型(dynamic type)
- 内存中对象的实际类型,运行时可知
- 决定调用的版本
- 如果表达式不是引用或指针,则它的动态类型和静态类型一致
- 不存在从基类到派生类的隐式类型转换
- 当基类含有一个或多个虚函数时,可使用dynamic_type<T&>( )/dynamic_type<T*>( )对尝试将 基类指针/引用 转换 派生类指针/引用
- dynamic_type<T&>(t) 转换引用失败时抛出bad_cast
- dynamic_type<T*>(t) 转换指针失败时返回空指针
- 自动类型转换只对指针和引用有效,在派生类类型和基类类型之间不存在
- 切掉(sliced down): 用派生类对象为一个基类对象初始化或赋值时,只有该派生类的对象中的基类部分会被拷贝、移动、赋值。
虚函数
- 基类中虚函数的返回值类型为类本身的引用或指针时,(派生类到基类的类型转换时可访问的时)派生类中的该虚函数可以与基类中的返回值不同,可返回自己的指针/引用。
- final修饰符(位于const 、引用修饰符后):禁止该虚函数被覆盖。
- 使用作用域运算符指定版本
- 纯虚函数 =0
- 含有纯虚函数的类是抽象基类(abstract base class),不能创建其对象。
访问控制
- 派生类的成员或友元只能通过派生类对象来访问基类的protected成员
- 派生说明符只影响派生类的用户,对派生类无影响。
- 只有在可以访问基类的共有成员时,派生类向基类的类型的转型才是可访问的。
- 派生类可以使用using声明改变其可访问的基类成员在该基类中的访问权。
- !!名字查找优先于类型检查,即使派生类成员和基类成员的形参列表不一致,同名基类成员也会被隐藏(要么不隐藏,要么全部隐藏)
- 可以使用using声明,将基类中的名字添加到该作用域,避免全部覆盖掉
class B{ public: void func(int a){ } void func(){ } }; class D : public B{ public: //加上:using B::func; 才能d.func(); void func(int a){ } }; int main(){ D d; d.func(); //错误无法访问 }
基类通常应该定义一个虚析构造函数
- 这样才能在“delete基类指针”时执行正确的析构函数
- 一个基类总是需要构造函数,并将其设为虚函数
- 虚析构函数将阻止合成移动操作(一个类定义了析构函数,即使它通过=default,也不会自动合成移动操作)
- 基类没有移动操作意味着派生类也没有
拷贝控制
派生类的拷贝和移动构造函数/赋值运算符在拷贝和移动自己的成员时,也要同时拷贝和移动基类部分的成员
合成的拷贝控制
- 派生类的合成的拷贝赋值/构造函数、移动复制运算符/构造函数,会先调用其父类的对应成员复制其父类部分
- 基类中
- 默认构造函数、拷贝复制/构造函数、析构函数 被删除/不可访问,则派生类中的对应成员也被删除
- 有不可访问/删除的析构函数, 则派生类中默认/拷贝构造函数将被删除
- 用=default请求合成移动操作时,如果基类对应操作是删除/不可访问的,则请求不能完成,该操作还是删除的
多重继承
- 基类的构造顺序与派生列表中基类的出现顺序保持一致
- 析构顺序正好与构造顺序相反
- 如果从多个基类继承了相同的构造函数(形参相同),将产生错误。这个类必须为该构造函数定义它自己的版本。
- 派生类如果定义自己的拷贝/赋值构造和赋值运算符,必须在完整的对象上执行拷贝、赋值或移动,不要忘记处理其基类的赋值(直接基类和间接基类)。派生类的合成版本会自动完成对其基类部分的操作。
- 任意的public继承的基类的指针可以指向一个派生类对象(都一样好)。
- 指针和引用的静态类型决定了通过它能够访问那些成员。
- 派生类的作用域嵌套在直接基类和间接基类的作用域中,名字的查找将由内向外。
虚继承
- 派生类可以通过它的两个间接基类分别继承同一个间接基类;或直接继承+间接继承一个基类
- 如果一个类在继承过程中出现多次,则派生类中将包含该类的多个子对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fw51XO3m-1624371512608)(…/virual_drive.png)]
#include<iostream> using namespace std; class Base0{ public: int a; }; class Base1 : public Base0{ public: int b; }; class Base2 : public Base0{ public: int b; }; class Foo : public Base1, public Base2{ public: int c; }; int main() { Foo f; f.a; //错误 f.b; //错误 Base0& b = a; //错误 }
- 虚继承解决上述问题,令某个类共享它的基类。共享的基类子对象称为虚基类(virtual base class),不论虚基类在继承体系中出现了多少次,在派生类中都只包含唯一一个共享的虚基类子对象。
#include<iostream> using namespace std; class Base0{ public: Base0(int a):a(a){ } int a; }; class Base1 : virtual public Base0{ public: Base1(int a, int b):Base0(a), b(b){ } int b; }; class Base2 : virtual public Base0{ public: Base2(int a, int b):Base0(a), b(b){ } int b; }; class Foo : public Base1, public Base2{ public: //Base1,Base2构造时传入的0,将被忽略,基类Base0需在构造函数初始化列表中初始化(否则默认初始化) Foo(int a, int b1, int b2, int c):Base0(a), Base1(0, b1), Base2(0, b2), c(c){ } int c; }; int main() { Foo f; f.a; //正确 f.b; //错误 Base0& b = f; //正确 }
- Foo对象只包含一个Base0的子对象
- 虚基类之影响从指定了虚基类的派生类中进一步派生出的类,不影响派生类本身
- 虚派生中,基类由最底层的派生类初始化。需在最底层的派生类的构造函数初始化列表中初始化(或者默认初始化),其他的基类的初始化列表中对其的初始化将被忽略。。
- 含有虚基类的对象的构造顺序:
- 先构造虚基类,最底层派生类构造函数构造提供初始值构造虚基类(或默认初始化)
- 有多个虚基类,按派生列表中出现的顺序构造
- 再按照基类在派生列表中出现的次序初构造
- 合成的拷贝和移动函数、赋值运算符也按该顺序
- 销毁顺序与构造顺序相反
更多相关内容 -
C++继承与派生类实验报告
2008-12-25 21:42:42本资源是C++继承与派生类实验报告,欢迎大家下载阿! -
C++实验报告四(继承与派生实验)
2020-11-24 19:59:26C++实验报告四(继承与派生实验) 前些天做实验,题目是:编写一个程序实现某小型公司的人员信息管理系统。该公司雇员(Employee)包括经理(Manager),技术人员(Technician)、销售员(Salesman)和销售部经理(Sales...C++实验报告四(继承与派生实验)
前些天做实验,题目是:编写一个程序实现某小型公司的人员信息管理系统。该公司雇员(Employee)包括经理(Manager),技术人员(Technician)、销售员(Salesman)和销售部经理(SalesManager)。要求存储这些人员的姓名、编号、级别、当月薪水,计算月薪并显示全部信息。程序要对所有人员有提升级别的功能。为简单起见,所有人员的初始级别均为1,然后进行升级,经理升为4级,技术人员和销售部经理升为3级,销售员仍为1级。月薪计算办法是 : 经理拿固定月薪8000元, 技术人员按每小时100元领取月薪,销售员按该当月销售额4% 提成,销售经理既拿固定月工资也领取销售提成,固定月工资为5000元, 销售提成为所管辖部门当月销售额的5% 。
以下是对象的继承和派生坑不得不跳啊!代码如下,欢迎指正,写完那么多行,发现眼睛都花了!
#include<cstring> #include <iostream> using namespace std; class Employee { public: Employee(string&); ~Employee(); string getname() { return name; } string getId() { return id; } int getlevel() { return level; } virtual double calculation_salary() { return 0; }; protected: char* name; //姓名 char* id; //编号 int level; //级别 double salary; //薪水 static int conut; //员工人数 static double Total_sales; //部门销售总额 }; Employee::Employee(string& name){ //cout << "员工构造成功:"; id = new char[12]; sprintf_s(id,12, "20201121%03d", conut); conut++; this->salary = 0; this->level = 1; this->name = new char[name.length() + 1]; strcpy_s(this->name,name.length()+1,name.c_str()); }; Employee::~Employee() { cout << "员工释放成功:"; //使用了new动态申请空间的数据类型必须使用delete释放空间;使用malloc申请的需要使用free释放 delete[] id; id = NULL; delete[] name; name = NULL; }; class Manager : public Employee { public: Manager (string& ); virtual double calculation_salary() { return salary; } }; Manager::Manager(string& name) : Employee(name) { cout << "经理构造成功\n"; salary = 8000; level += 3; }; class Techician : public Employee { public: Techician(string&, float); virtual double calculation_salary() { salary = 100.0 * hours; return salary; } protected: float hours; }; Techician::Techician(string& name ,const float hours=0) : Employee(name) { cout << "技术人员构造成功\n"; this->hours = hours; level += 2; }; class Salesman :public Employee { public: Salesman(string &,double); virtual double calculation_salary() { salary = Monthly_sales * 0.04; return salary; } private: double Monthly_sales; }; Salesman::Salesman(string &name,double Monthly_sales = 0.0):Employee(name){ cout << "销售员构造成功!\n"; this->Monthly_sales = Monthly_sales; Total_sales += Monthly_sales; } class SalesManager :public Employee { public: SalesManager(string&); virtual double calculation_salary() { salary = 5000 + Total_sales * 0.05; return salary; } }; SalesManager::SalesManager(string& name):Employee(name){ level += 2; cout << "销售部经理构造成功!\n"; } int Employee::conut = 1; double Employee::Total_sales = 0.0; void oputEmployee(Employee* Position, string& occupation) { cout << "姓名:" << Position->getname() << "\t编号:" << Position->getId() << "\t等级:" << Position->getlevel() << "\t月薪:" << Position->calculation_salary() << "\t岗位:" << occupation << endl; } int main() { cout << "——————请输入五个新员工的的姓名、工作岗位——————" << endl; cout << "—————岗位有经理、技术人员、销售员、销售部经理————" << endl; Employee* Emp[5] = { NULL }; string name_Position[5][2]; for (int i = 0; i < 5; i++) { double temp = 0.0; cout << "姓名:"; cin >> name_Position[i][0]; while (true) { cout << "岗位:"; cin >> name_Position[i][1]; if (name_Position[i][1] == "经理"|| name_Position[i][1] == "技术人员"|| name_Position[i][1] == "销售员"|| name_Position[i][1] == "销售部经理") break; cout << "没有这个岗位!"; } if (name_Position[i][1] == "经理") { Emp[i] = new Manager(name_Position[i][0]); } else if (name_Position[i][1] == "技术人员") { cout << "本月上班时长:"; cin >> temp; Emp[i] = new Techician(name_Position[i][0], (float)temp); } else if (name_Position[i][1] == "销售员") { cout << "月销售额:"; cin >> temp; Emp[i] = new Salesman(name_Position[i][0], temp); } else if (name_Position[i][1] == "销售部经理") { Emp[i] = new SalesManager(name_Position[i][0]); } } cout << "————————————————————————————" << endl; for (int i = 0; i < 5; i++) { oputEmployee(Emp[i],name_Position[i][1]); } cout << "辛苦了,已经完成管理!"; for (int i = 0; i < 5; i++) { delete Emp[i]; Emp[i] = NULL; } return 0; }
在这里写的代码很多感觉都是废代码,感觉代码十分笼统,教材书上的感觉比我少多了。- 定义数据成员类型
char* name; //姓名 char* id; //编号 int level; //级别 double salary; //薪水 static int conut; //员工人数 static double Total_sales; //部门销售总额
水平比较低,实在是折腾不来string类型是数据,开始也是使用string类型来定义name,id数据的,但是在构造函数对其赋初值时,各种报错(错误类型大概有指针悬空、内存溢出、还用“.c_str”安全模式等问题),人都会傻掉,所以改用char*类型数据,同时定义两个静态数据用于存储员工人数conut(产生员工ID)和存储销售部销售总额Total_sales(计算销售部经理的薪水),同时注意static数据的初始化,参考链接。
- 构造函数和构析函数
Employee::Employee(string& name){ //cout << "员工构造成功:"; id = new char[12]; sprintf_s(id,12, "20201121%03d", conut); conut++; this->salary = 0; this->level = 1; this->name = new char[name.length() + 1]; strcpy_s(this->name,name.length()+1,name.c_str()); }; Employee::~Employee() { cout << "员工释放成功:"; //使用了new动态申请空间的数据类型必须使用delete释放空间;使用malloc申请的需要使用free释放 delete[] id; id = NULL; delete[] name; name = NULL; };
每个人的想法不同,有些人觉得有这个构造函数就没有下面子类构造函数的事了,其实我也是有点这种感觉,但是,考虑到自己是初学者,为了避免错误的发生最好的办法就是把重点集中起来,同时可以把注意力集中到这段代码去,把重点分散开来,一旦出现错误,感觉满门忠烈,累死id就是自己了,这样的代价就是,感觉代码头重脚轻,不过后面自己去优化代码就变得有必要了,自己看得也舒服点,同时使用"string &"引用减少内存占用。构析函数中对使用了new分配空间的数据进行释放,使用delete[],注意的是将指针指向NULL,当然使用string类型的就省事了。
- 使用sprintf_s()函数将数值转化为字符串产生员工ID,了解sprintf和sprintf_s的区别可以参考这个链接
sprintf_s(id,12, "20201121%03d", conut);
使用前需要将库函数包含进来,相比与其他的转化类型我更喜欢这种类型,因为输出的格式比较自由。
- 虚函数和纯虚函数的选择
virtual double calculation_salary() { return 0; };
其实对于这两个东西在本次实验中都是可以考虑的,首先我没有需要对基类(Employee)产生对象的需求,所以完全可以让他作接口,负责函数的统一,但是又考虑到基类还可以有大量的成员数据,对于这些成员数据总不能在建立一个基类用于继承吧,所以选择定义为虚基类,方便成员函数“calculation_salary()”进行重写。
- 主函数
int main() { cout << "——————请输入五个新员工的的姓名、工作岗位——————" << endl; cout << "—————岗位有经理、技术人员、销售员、销售部经理————" << endl; Employee* Emp[5] = { NULL }; string name_Position[5][2]; for (int i = 0; i < 5; i++) { double temp = 0.0; cout << "姓名:"; cin >> name_Position[i][0]; while (true) { cout << "岗位:"; cin >> name_Position[i][1]; if (name_Position[i][1] == "经理"|| name_Position[i][1] == "技术人员"|| name_Position[i][1] == "销售员"|| name_Position[i][1] == "销售部经理") break; cout << "没有这个岗位!"; } if (name_Position[i][1] == "经理") { Emp[i] = new Manager(name_Position[i][0]); } else if (name_Position[i][1] == "技术人员") { cout << "本月上班时长:"; cin >> temp; Emp[i] = new Techician(name_Position[i][0], (float)temp); } else if (name_Position[i][1] == "销售员") { cout << "月销售额:"; cin >> temp; Emp[i] = new Salesman(name_Position[i][0], temp); } else if (name_Position[i][1] == "销售部经理") { Emp[i] = new SalesManager(name_Position[i][0]); } } cout << "————————————————————————————" << endl; for (int i = 0; i < 5; i++) { oputEmployee(Emp[i],name_Position[i][1]); } cout << "辛苦了,已经完成管理!"; for (int i = 0; i < 5; i++) { delete Emp[i]; Emp[i] = NULL; } return 0; }
本次实验就简单的实现五个员工的对象数组(一个经理,一个销售部经理,一个技术人员,两个销售员),通过创建基类对象指针Emp[5]—>向上转型(多态性)—>输出对象信息"oputEmployee(Employye *,Position)"—>释放空间。这里有个问题是因为使用向上转型,发现构析函数无法自动调用(自己不知道是啥原因,后面再搞),所以,需要手动调用基类的构析函数。
for (int i = 0; i < 5; i++) { delete Emp[i]; Emp[i] = NULL; } return 0;
- 实验结果
突然感觉做销售好难呀! -
继承与派生总结
2018-06-19 18:07:34c++继承与派生总结 面向对象的程序设计中提供了类的继承机制,允许程序员在保持原有类特性的基础上,进行更具体、更详细的类的定义。以原有的类为基础产生新的类,我们就说新类继承了原有类的特征,也就是说从原有类... -
c++继承和派生(个人总结笔记)
2021-08-19 19:54:351、C++继承的概念及语法 继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产。 继承(Inheritance) 可以理解为一个类从另一个类获取成员变量和成员函数的过程...c++继承和派生(个人总结笔记)
一、继承和派生
1、C++继承的概念及语法
-
继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产。
-
继承(Inheritance) 可以理解为一个类从另一个类获取成员变量和成员函数的过程。例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数。被继承的类称为父类或基类,继承的类称为子类或派生类。
-
继承方式包括 public(公有的)、private(私有的)和 protected(受保护的),此项是可选的,如果不写,那么默认为 private。
-
demo语法
class 派生类名:[继承方式] 基类名{
派生类新增加的成员
};//基类 Pelple class People{ } //派生类 Student class Student: public People{ }
Student 类继承了 People 类的成员成员函数 。这些继承过来的成员,可以通过子类对象访问,就像自己的一样。
2、C++继承权限和继承方式
public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限- public继承方式
- 基类中所有 public 成员在派生类中为 public 属性;
- 基类中所有 protected 成员在派生类中为 protected 属性;
- 基类中所有 private 成员在派生类中不能使用。
- protected继承方式
- 基类中的所有 public 成员在派生类中为 protected 属性;
- 基类中的所有 protected 成员在派生类中为 protected 属性;
- 基类中的所有 private 成员在派生类中不能使用。
- private继承方式
- 基类中的所有 public 成员在派生类中均为 private 属性;
- 基类中的所有 protected 成员在派生类中均为 private 属性;
- 基类中的所有 private 成员在派生类中不能使用。
- 由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。
- 改变访问权限
using 关键字可以改变基类成员在派生类中的访问权限,例如将 public 改为 private、将 protected 改为 public。
3、C++继承时的名字遮蔽
如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,那么就会遮蔽从基类继承过来的成员。所谓遮蔽,就是在派生类中使用该成员(包括在定义派生类时使用,也包括通过派生类对象访问该成员)时,实际上使用的是派生类新增的成员,而不是从基类继承来的。
- 基类成员函数和派生类成员函数不构成重载
基类成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数要引起注意,不管函数的参数如何,只要名字一样就会造成遮蔽。换句话说,基类成员函数和派生类成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数,不管它们的参数是否一样。
4、C++派生类构造函数
- 基类的成员函数可以被继承,可以通过派生类的对象访问;
- 类的构造函数不能被继承。
- 在派生类的构造函数中调用基类的构造函数。
Student::Student(char *name, int age, float score): People("小明", 16), m_score(score){ }
- 派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
- 通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。 换句话说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。
5、派生类的析构函数
- 和构造函数类似,析构函数也不能被继承。(不用程序员显示调用、编译器负责)
- 外析构函数的执行顺序和构造函数的执行顺序也刚好相反。
- 创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。
- 而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构函数,再执行基类析构函数。
6、C++虚继承和虚基类
- 多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。(容易产生问题,命名冲突。)
- 虚继承(Virtual Inheritance)
为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。在继承方式前面加上 virtual 关键字就是虚继承
//间接基类A class A{ protected: int m_a; }; //直接基类B class B: virtual public A{ //虚继承 protected: int m_b; };
- 虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class)
7、C++虚继承时的构造函数
- 虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。
- 这跟普通继承不同,在普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
D::D(int a, int b, int c, int d): A(a), B(90, b), C(100, c), m_d(d){ } void D::display(){ cout<<"m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl; }
- 例子:
- 在最终派生类 D 的构造函数中,除了调用 B 和 C 的构造函数,还调用了 A 的构造函数,这说明 D 不但要负责初始化直接基类 B 和 C,还要负责初始化间接基类 A。
- 而在以往的普通继承中,派生类的构造函数只负责初始化它的直接基类,再由直接基类的构造函数初始化间接基类
-
-
c++程序设计 继承与派生实验(二)
2012-04-25 21:52:24c++程序设计 继承与派生实验(二) 1. 进一步掌握类的派生与继承的概念、应用方法 2. 掌握继承方式对成员访问权限的影响 3. 掌握虚基类的用法 -
C++实验继承与派生
2020-11-07 12:42:24实验名称 继承与派生 日期 2019年 05 月05 日 一、实验目的: 1. 学习定义和使用类的继承关系,定义派生类。 2. 理解类的访问控制与类的成员访问的关系。 3. 熟悉不同继承方式下对基类成员的访问控制。 4. 学习...桂 林 理 工 大 学
实 验 报 告
实验名称 继承与派生 日期 2019年 05 月05 日
一、实验目的:
1. 学习定义和使用类的继承关系,定义派生类。
2. 理解类的访问控制与类的成员访问的关系。
3. 熟悉不同继承方式下对基类成员的访问控制。
4. 学习利用虚基类解决二义性问题。
二、实验环境:
Visual C++三、实验内容:
(写出主要的内容)
1. 定义一个基类Animal,有私有整形成员变量age,构造其派生类dog,在其成员函数SetAge(int n)中直接给age赋值,看看会有什么问题,把age改为公有成员变量,还会有问题吗?改为保护类型呢?改变继承方式呢?#include<iostream> using namespace std; class Animal { protected: int age; public: Animal(); ~Animal(); }; Animal::Animal() { cout<<"构造了一个Animal!"<<endl; } Animal::~Animal() { cout<<"析构了一个Animal!"<<endl; } class Dog:public Animal { public: Dog(); ~Dog(); void SetAge(int n); }; Dog::Dog() { cout<<"构造了一个Dog!"<<endl; } Dog::~Dog() { cout<<"析构了一个Dog!"<<endl; } void Dog::SetAge(int n) { age=n; cout<<"The Dog's age:"<<n<<endl; } int main() { Dog a; a.SetAge(5); }
输出结果:构造了一个Animal!
构造了一个Dog!
The Dog’s age:5
析构了一个Dog!
析构了一个Animal!
2. 通过在上例的构成函数、析构函数中添加提示信息,或用debug调试功能观察构造函数和析构函数的执行情况,并分析原因。#include<iostream> using namespace std; class Animal { protected: int age; public: Animal(); ~Animal(); }; Animal::Animal() { cout<<"构造了一个Animal!"<<endl; } Animal::~Animal() { cout<<"析构了一个Animal!"<<endl; } class Dog:public Animal { public: Dog(); ~Dog(); int SetAge(); void PutAge(); }; Dog::Dog() { cout<<"构造了一个Dog!"<<endl; } Dog::~Dog() { cout<<"析构了一个Dog!"<<endl; } int Dog::SetAge() { int n; cout<<"Please input the Dog's age:"; cin>>n; age=n; return age; } void Dog::PutAge() { cout<<"The Dog's age:"<<age<<endl; } int main() { Dog a; a.SetAge(); a.PutAge(); }
输出结果:构造了一个Animal!
构造了一个Dog!
Please input the Dog’s age:6
The Dog’s age:6
析构了一个Dog!
析构了一个Animal!
3. 定义一个车(vehicle)基类,具有MaxSpeed、Weight等成员变量,Run、Stop等成员函数,由此派生出自行车(bicycl)类、汽车(motorcar)类。自行车类有高度(Height)等属性,汽车类有座位数(SeatNum)等属性。从和派生出摩托车(motorcycle)类,在继承过程中注意把设置为虚基类。如果不把vehice设置为虚基类,会有什么问题?并分析原因。
Motorcycle无法运行,因为产生了二义性#include<iostream> using namespace std; class Vehicle { private: int MaxSpeed; int Weigth; public: Vehicle(); ~Vehicle(); void Run(); void Stop(); }; Vehicle::Vehicle() { MaxSpeed=0; Weigth=0; cout<<"构造了一个Vehicle"<<endl; } Vehicle::~Vehicle() { cout<<"析构了一个Vehicle"<<endl; } void Vehicle::Run() { cout<<"Vehicle开始运行!"<<endl; } void Vehicle::Stop() { cout<<"Vehicle停止运行!"<<endl; } class bicycle:virtual public Vehicle { private: int Height; public: bicycle(); ~bicycle(); }; bicycle::bicycle() { cout<<"构造了一个bicycle"<<endl; } bicycle::~bicycle() { cout<<"析构了一个bicycle"<<endl; } class motocar:virtual public Vehicle { private: int SetNumber; public: motocar(); ~motocar(); }; motocar::motocar() { cout<<"构造了一个motocar"<<endl; } motocar::~motocar() { cout<<"析构了一个motocar"<<endl; } class motorcycle:public bicycle,public motocar { public: motorcycle(); ~motorcycle(); }; motorcycle::motorcycle() { cout<<"构造了一个motorcycle"<<endl; } motorcycle::~motorcycle() { cout<<"析构了一个motorcycle"<<endl; } int main() { Vehicle a; bicycle b; motocar c; motorcycle d; a.Run() ; a.Stop() ; b.Run() ; b.Stop() ; c.Run() ; c.Stop() ; d.Run() ; d.Stop() ; }
输出结果:
构造了一个Vehicle
构造了一个Vehicle
构造了一个bicycle
构造了一个Vehicle
构造了一个motocar
构造了一个Vehicle
构造了一个bicycle
构造了一个motocar
构造了一个motorcycle
Vehicle开始运行!
Vehicle停止运行!
Vehicle开始运行!
Vehicle停止运行!
Vehicle开始运行!
Vehicle停止运行!
Vehicle开始运行!
Vehicle停止运行!
析构了一个motorcycle
析构了一个motocar
析构了一个bicycle
析构了一个Vehicle
析构了一个motocar
析构了一个Vehicle
析构了一个bicycle
析构了一个Vehicle
析构了一个Vehicle
四、 心得体会:
1、学会了如何定义和使用类的继承关系,定义派生类。
2、理解了类的访问控制与类的成员访问的关系。
3、熟悉了不同继承方式下对基类成员的访问控制。
4、学会了如何利用虚基类解决二义性问题。 -
C++实验五 继承与派生的应用 课程 实验报告
2010-06-11 10:52:17C++实验五 继承与派生的应用 课程 实验报告 作业参考的良品! -
C++(类,继承派生,多态性)实验报告
2011-03-31 12:33:57包含了C++实验的模板与答案,内容有类与对象,继承与派生,数组与指针,多态性,异常处理等,该资源内容详细清楚,较于学习掌握。 (希望大家不要下载这个文件,里面有些东西是无用的,我重新传一份上去) -
c++类的继承与派生--实验报告.doc
2021-10-12 16:43:48c++类的继承与派生--实验报告.doc -
c++派生类与继承实验报告.pdf
2021-11-15 09:49:29c++派生类与继承实验报告 -
造福学弟学妹系列:C++继承和派生实验报告
2018-05-22 21:03:14将我们学校C++课的实验报告copy上来,希望能给未来的学弟学妹做参考 教材:C++语言程序设计(第四版) 清华大学出版社 一、实验目的 1、学习声明和使用类的继承关系,声明派生类 2、熟悉不同继承方式下对基类... -
C++实验五:派生与继承
2021-12-11 23:24:48本次实验学习了派生与继承,要求掌握派生类的声明方法和派生类构造函数的定义方法;同时掌握不同方式下,基类成员在派生类中的访问属性和访问规则 代码如下: #include<iostream> using namespace std; ... -
c++继承与派生
2013-01-19 20:57:51c++继承与派生,上机操作题,帮助理解继承与派生 -
C++继承与派生(谭浩强11.8-11.10)
2020-05-06 17:06:38例11.8多重继承派生类的构造函数: #include <iostream> using namespace std; #include<string> class Teacher//声明基类 { public://基类公用成员 Teacher(string nam,int a,string t )//基类构造... -
C++实验6 继承与派生(一)
2019-05-17 16:49:48实验名称:实验6 继承与派生(一) 所使用的开发工具及环境:PC机一套 Visual Studio 2010 实验要求: 1.硬件基本配置:Intel PentiumIII以上级别的CPU,大于64MB的内存。 2.软件要求:Window 2000操作系统,Visual ... -
c++派生类及继承实验报告.doc
2021-10-07 08:12:29c++派生类及继承实验报告.doc -
C++学习心得(5)继承与派生
2014-10-11 13:11:50C++学习心得(5)继承与派生 ——from 谭浩强.C++面向对象程序设计(第一版) 2014/10/9 面向对象的程序设计的4个主要特点: 抽象、封装、继承、多态性 5.1 继承与派生的概念 在C++中,继承就是在一个已存在... -
C++实验:派生与继承
2021-11-02 00:21:542) 掌握派生类的声明与定义方法。 3) 熟悉公有派生和私有派生的访问特性。 4) 学习虚基类在解决二义性问题中的作用。 二、实验内容 1) 定义一个基类MyArray,基类中可以存放一组整数。 class MyArray { ... -
类的继承、派生、多态性C++课程实验报告.pdf
2021-10-14 11:29:16类的继承、派生、多态性C++课程实验报告.pdf -
NEFU C++实验四派生类与继承(锐格)
2022-04-02 18:05:17下面的程序可以输出ASCII字符与所对应的数字的对照表,修改下列程序,使其可以输出字母a到字母z。 输入输出说明: 输出:每隔12个字母换行,设置输出的域宽为4 ASCII value-char 97a 98b 99c 100d 101e 102f ... -
c++实验三:继承与派生
2020-05-31 00:40:061、 掌握类继承与派生关系以及实现方法,理解类的层次结构; 2、 掌握派生类构造函数初始化基类成员和对象成员的方法; 3、 掌握类型兼容规则,掌握派生类的复制构造函数的定义。 4、 掌握多重继承和虚基类。 二.一... -
天津理工C++实验报告齐全
2019-03-12 21:22:56实验一:类和简单对象 , 实验二 :复杂形式的对象 ,实验三 :派生与继承, 实验四:多态程序设计 , 实验五: 1/O 流类库 , 实验六 :模板 , -
实验3-继承与派生
2014-04-28 15:35:44C++面向对象程序设计实验3继承与派生 需要的哦朋友可以下载 -
C++实验7继承与派生(二)
2019-05-24 22:09:23所使用的开发工具及环境:PC机一套 Visual Studio 2010 实验要求: 1.硬件基本配置:Intel PentiumIII以上级别的CPU,大于64MB的内存。...5.写实验报告 实验目的: 1. 理解继承和派生的概念; 2. 理解继... -
C++实验报告三:类的继承
2021-04-21 16:22:57掌握类继承与派生关系以及实现方法,理解类的层次结构; 掌握派生类构造函数初始化基类成员和对象成员的方法; 掌握类型兼容规则,掌握派生类的复制构造函数的定义; 掌握多重继承和虚基类。 【实验内容】 ... -
C++上机 派生类与继承
2021-12-11 22:59:48一、实验目的 1. 掌握派生类的声明方法和派生类构造函数的定义方法。 2. 掌握不同方式下,基类成员在派生类中的访问属性和访问规则。 二、实验内容 #include<iostream> using namespace std; class ... -
实验四-继承与派生
2022-04-25 10:50:10初学c++,记录基类、派生类、虚基类的笔记