精华内容
下载资源
问答
  • C++继承与派生总结
    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++继承与派生实验报告,欢迎大家下载阿!
  • C++实验报告四(继承与派生实验

    千次阅读 2020-11-24 19:59:26
    C++实验报告四(继承与派生实验) 前些天做实验,题目是:编写一个程序实现某小型公司的人员信息管理系统。该公司雇员(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:34
    c++继承与派生总结 面向对象的程序设计中提供了类的继承机制,允许程序员在保持原有类特性的基础上,进行更具体、更详细的类的定义。以原有的类为基础产生新的类,我们就说新类继承了原有类的特征,也就是说从原有类...
  • 1、C++继承的概念及语法 继承是类类之间的关系,是一个很简单很直观的概念,现实世界中的继承类似,例如儿子继承父亲的财产。 继承(Inheritance) 可以理解为一个类从另一个类获取成员变量和成员函数的过程...

    一、继承和派生

    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 指定继承方式
    不同的继承方式会影响基类成员在派生类中的访问权限

    1. public继承方式
    • 基类中所有 public 成员在派生类中为 public 属性;
    • 基类中所有 protected 成员在派生类中为 protected 属性;
    • 基类中所有 private 成员在派生类中不能使用。
    1. protected继承方式
    • 基类中的所有 public 成员在派生类中为 protected 属性;
    • 基类中的所有 protected 成员在派生类中为 protected 属性;
    • 基类中的所有 private 成员在派生类中不能使用。
    1. 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++程序设计 继承与派生实验(二) 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++实验五 继承与派生的应用 课程 实验报告 作业参考的良品!
  • 包含了C++实验的模板答案,内容有类对象,继承与派生,数组指针,多态性,异常处理等,该资源内容详细清楚,较于学习掌握。 (希望大家不要下载这个文件,里面有些东西是无用的,我重新传一份上去)
  • c++类的继承与派生--实验报告.doc
  • c++派生与继承实验报告
  • 造福学弟学妹系列:C++继承派生实验报告

    千次阅读 多人点赞 2018-05-22 21:03:14
    将我们学校C++课的实验报告copy上来,希望能给未来的学弟学妹做参考 教材:C++语言程序设计(第四版) 清华大学出版社 一、实验目的 1、学习声明和使用类的继承关系,声明派生类 2、熟悉不同继承方式下对基类...
  • 本次实验学习了派生与继承,要求掌握派生类的声明方法和派生类构造函数的定义方法;同时掌握不同方式下,基类成员在派生类中的访问属性和访问规则 代码如下: #include<iostream> using namespace std; ...
  • c++继承与派生

    2013-01-19 20:57:51
    c++继承与派生,上机操作题,帮助理解继承与派生
  • 例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
  • C++学习心得(5)继承与派生

    千次阅读 2014-10-11 13:11:50
    C++学习心得(5)继承与派生 ——from 谭浩强.C++面向对象程序设计(第一版) 2014/10/9 面向对象的程序设计的4个主要特点:  抽象、封装、继承、多态性 5.1 继承与派生的概念 在C++中,继承就是在一个已存在...
  • C++实验派生与继承

    2021-11-02 00:21:54
    2) 掌握派生类的声明定义方法。 3) 熟悉公有派生和私有派生的访问特性。 4) 学习虚基类在解决二义性问题中的作用。 二、实验内容 1) 定义一个基类MyArray,基类中可以存放一组整数。 class MyArray { ...
  • 类的继承派生、多态性C++课程实验报告.pdf
  • 下面的程序可以输出ASCII字符所对应的数字的对照表,修改下列程序,使其可以输出字母a到字母z。 输入输出说明: 输出:每隔12个字母换行,设置输出的域宽为4 ASCII value-char 97a 98b 99c 100d 101e 102f ...
  • c++实验三:继承与派生

    千次阅读 多人点赞 2020-05-31 00:40:06
    1、 掌握类继承与派生关系以及实现方法,理解类的层次结构; 2、 掌握派生类构造函数初始化基类成员和对象成员的方法; 3、 掌握类型兼容规则,掌握派生类的复制构造函数的定义。 4、 掌握多重继承和虚基类。 二.一...
  • 实验一:类和简单对象 , 实验二 :复杂形式的对象 ,实验三 :派生与继承实验四:多态程序设计 , 实验五: 1/O 流类库 , 实验六 :模板 ,
  • 实验3-继承与派生

    2014-04-28 15:35:44
    C++面向对象程序设计实验3继承与派生 需要的哦朋友可以下载
  • C++实验7继承与派生(二)

    千次阅读 2019-05-24 22:09:23
    所使用的开发工具及环境:PC机一套 Visual Studio 2010 实验要求: 1.硬件基本配置:Intel PentiumIII以上级别的CPU,大于64MB的内存。...5.写实验报告 实验目的: 1. 理解继承派生的概念; 2. 理解继...
  • 掌握类继承与派生关系以及实现方法,理解类的层次结构; 掌握派生类构造函数初始化基类成员和对象成员的方法; 掌握类型兼容规则,掌握派生类的复制构造函数的定义; 掌握多重继承和虚基类。 【实验内容】 ...
  • C++上机 派生与继承

    2021-12-11 22:59:48
    一、实验目的 1. 掌握派生类的声明方法和派生类构造函数的定义方法。 2. 掌握不同方式下,基类成员在派生类中的访问属性和访问规则。 二、实验内容 #include<iostream> using namespace std; class ...
  • 实验四-继承与派生

    2022-04-25 10:50:10
    初学c++,记录基类、派生类、虚基类的笔记

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,332
精华内容 532
关键字:

c++继承与派生实验报告

c++ 订阅