精华内容
下载资源
问答
  • 所谓隐藏是指使用常规的调用方法,派生类对象访问这个函数时,会优先访问派生类中的这个函数,基类中的这个函数对派生类对象来说是隐藏起来的。 但是隐藏并不意味这不存在或完全不可访问。通过 b->Base::func()访问...
  • 派生类的数据成员由所有基类的数据成员与派生类新增的数据成员共同组成,如果派生类新增成员中包括其他类的对象(子对象),派生类的数据成员中实际上还间接包括了这些对象的数据成员。
  • 基类对象与派生类对象的使用关系 派生类对象作为基类对象处理 由于派生类具有所有基类的成员所以把派生类的对赋给基类对象是合理的不过要求这种继承方式必须是public方式但是反过来赋值会使基类中一具有派生类的成员...
  • 派生类

    2020-06-02 21:12:55
    类型转换派生类的构造函数派生类的析构函数派生类的访问当派生类中与基类有相同成员时:当派生类中无与基类有相同成员时: 派生类 派生,为什么要派生? 派生的目的: 当新问题出现,原有的程序无法解决或者无法完全...

    派生,为什么要派生?

    派生的目的
    当新问题出现,原有的程序无法解决或者无法完全解决时,需要对原有的程序进行改造。
    单继承时派生的定义:语法:class 派生类名:继承方式 基类名
    继承方式:
    继承方式存在三种,分别是公有继承,私有继承和保护继承。
    公有继承:基类的public和protected成员访问属性在派生类中保持不变,基类的private成员不可直接访问。
    私有继承:基类的public和protected成员以private身份出现在派生类中。
    保护继承:基类的public和protected成员以protected身份出现在派生类中,基类的private成员不可直接访问。派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。通过派生类的对象不能直接访问从基类继承的任何成员。

    类型转换

    公有派生类对象可以被当做基类的对象使用,反之不可。因为公有派生类继承了基类的东西,即派生类的内容比基类只多不少,基类的对象访问接口和派生类的一样,因此可以直接当做基类的对象使用。
    派生类的对象可以隐含转化为基类对象
    派生类的对象可以初始化基类的引用
    派生类的指针可以隐含转化为基类的指针
    通过基类的对象名,指针只能使用从基类继承的成员

    派生类的构造函数

    默认情况下:基类的构造函数不能被继承,派生类需要自己定义构造函数。当基类有默认构造函数时,派生类构造函数可以不向基类传递参数,创建派生类的对象时,基类的默认构造函数被调用。而派生类继承的需要初始参数的基类成员函数会由派生类传递参数给基类,再由基类的构造函数初始化这些成员函数,而派生类新增部分的初始化由函数体和初始化列表完成。
    (c++11可以使用using语句继承基类的构造函数,但只能初始化从基类继承的成员:
    using 基类名 ::基类构造函数名)

    单继承时派生类构造函数语法:
    派生类名::派生类名(基类初始化所需的形参,本类成员所需的形参):基类名(参数表),
    本类成员初始化列表
    {
    其他初始化
    }
    如下Base2中的构造函数:

    #include<iostream>
    using namespace std;
    class Base1{
    	public:
    		Base1(){
    			b=0;
    			cout<<"Base1 is constructed..."<<endl;
    		}
    		Base1(int i){
    			b=i;
    			cout<<"Base1 is constructed..."<<endl;
    		}
    		~Base1(){
    			cout<<"Base1 is destructed..."<<endl; 
    		}
    		void print() const{
    			cout<<b<<endl;
    		}
    	private:
    		int b;
    	
    };
    class Base2:public Base1{
    	public:
    		Base2(){
    			c=0;
    			cout<<"Base2 is constructed..."<<endl;
    		}
    		Base2(int i,int j):Base1(i),c(j){
    			cout<<"Base2 is constructed..."<<endl;
    		}
    		~Base2(){
    			cout<<"Base2 is destructed..."<<endl; 
    		}
    		void print() const{
    			Base1::print();
    			cout<<c<<endl;
    		}
    	private:
    		int c;
    	
    };
    int main(){
    	Base2 b(1,2);
    	b.print();
    	return 0;
    }
    

    继承时派生类将基类所需的初始化参数传递给基类进行初始化,然后初始化自己的列表。上述程序过程中,生成Base2的对象,由于有两个参数,因此调用有两个参数的构造函数Base2(int i,int j):Base1(i),c(j){},这个函数要向Base1的构造函数传递参数,因此先进行Base1的构造,运行结果如下:
    在这里插入图片描述
    由上述可知派生类构造函数的执行顺序:

    1.调用基类的构造函数,顺序按照它们被继承时声明的顺序,先声明先构造。
    2.对初始化列表中的成员进行初始化,顺序按照它们在类中定义的顺序,先定义先构造。
    对象成员初始化时自动调用其所属类的构造函数,由初始化列表提供参数。
    3.初始化派生类的构造函数体中的内容。

    以下程序进行构造顺序的知识点加强:

    #include<iostream>
    using namespace std;
    class Base1{
    	public:
    	Base1(int a){
    		cout<<"Base1 is constructed..."<<a<<endl; 
    	}
    }; 
    class Base2{
    	public:
    	Base2(int a){
    		
    		cout<<"Base2 is constructed..."<<a<<endl; 
    	}
    }; 
    class Base3{
    	public:
    	Base3(){
    		cout<<"Base3 is constructed..."<<endl; 
    	}
    }; 
    class Depre:public Base1,public Base2,public Base3{
    	public: 
    	Depre(int a,int b,int c,int d):Base1(a),Base2(b),number1(c),number2(d){}
    	private:
    		Base1 number1;
    		Base2 number2;
    		Base3 number3;
    };
    int main(){
    	Depre d(5,2,6,7);
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述

    派生类的析构函数

    析构函数也不能被继承,派生类如果需要,则需要自行声明析构函数,声明方式与无继承关系时的析构函数相同。(不用显示调用基类的析构函数,系统会自动隐式调用)
    析构的顺序与构造时正好相反。
    构造时先构造基类继承过来成员,再构造自身函数体的内容,而析构时先析构本类自己的成员,然后再析构继承过来的成员(析构自己的成员时最后被构造的最先析构,析构继承的成员时,最后继承的最先被析构)

    #include<iostream>
    using namespace std;
    class Base1{
    	public:
    	Base1(int a){
    		cout<<"Base1 is constructed..."<<a<<endl; 
    	}
    	~Base1(){
    		cout<<"Base1 is destructed..."<<endl; 
    	} 
    }; 
    class Base2{
    	public:
    	Base2(int a){
    		
    		cout<<"Base2 is constructed..."<<a<<endl; 
    	}
    	~Base2(){
    		cout<<"Base2 is destructed..."<<endl; 
    	} 
    }; 
    class Base3{
    	public:
    	Base3(){
    		cout<<"Base3 is constructed..."<<endl; 
    	}
    	~Base3(){
    		cout<<"Base3 is destructed..."<<endl; 
    	} 
    }; 
    class Depre:public Base1,public Base2,public Base3{
    	public: 
    	Depre(int a,int b,int c,int d):Base1(a),Base2(b),number1(c),number2(d){}
    	private:
    		Base1 number1;
    		Base2 number2;
    		Base3 number3;
    };
    int main(){
    	Depre d(5,2,6,7);
    	return 0;
    }
    

    运行结果如下:
    在这里插入图片描述
    如上所示,先析构了自己本类的成员,最后构造的最先析构,按number3,number2,number1的顺序,再析构继承过来的成员,最后继承的最先析构,按Base3,Base2,Base1的顺序。

    派生类的访问

    当派生类中与基类有相同成员时:

    若未特别限定,则通过派生类对象使用的是派生类中的同名成员(同名隐藏规则),即默认访问多个同名函数中所属派生类的那个。若要通过派生类对象访问基类中被隐藏的同名函数,可以使用基类名加作用域操作符::来限定。比如以下 d.Base1::fun();,实现调用基类被隐藏的fun()函数。

    #include<iostream>
    using namespace std;
    class Base1{
    	public:
    	int var;
    	void fun(){
    		cout<<"number of Base1"<<endl;
    	} 
    };
    class Base2{
    	public:
    	int var;
    	void fun(){
    		cout<<"number of Base2"<<endl;
    	} 
    };
    class Derived:public Base1,public Base2{
    	public:
    		int var;
    		void fun(){
    			cout<<"number of Derived"<<endl;
    		}
    }; 
    int main(){
    	Derived d;
    	d.fun();//同名隐藏原则,默认调用派生类的同名函数
    	d.Base1::fun();
    }
    

    运行结果如下:
    在这里插入图片描述

    当派生类中无与基类有相同成员时:

    同时从不同基类继承了同名成员时访问成员存在二义性问题,即继承之后,这些同名函数如何区分,如何访问成了问题。(可以采用用类名限定来解决二义性的问题)

    class A{
    	public:
    	void q();
    };
    class B{
    	public:
    	void q();
    	void w();
    };
    class C:public A,public B{
    	public:
    	void w();
    	void e();
    };
    

    如果此时定义一个C c;,那么c.q();就存在二义性问题,而c.w()则无二义性(同名隐藏原则)。

    编写程序的时候应该要避免出现二义性和冗余问题(同时多次继承同一个类出现数据重复等等诸类情况)。

    展开全文
  • 主要介绍了详解C++中基类与派生类的转换以及虚基类,是C++入门学习中的基础知识,需要的朋友可以参考下
  • protected 与 public 和 private 一样是用来声明成员的访问权限的。由protected声明的成员...在定义一个派生类时将基类的继承方式指定为protected的,称为保护继承,用保护继承方式建立的派生类称为保护派生类(protec
  • 题目:设计基类点类(Point)、直接派生类圆类(Circle)、间接派生类圆柱体类(Cylinder),将求面积的函数(area)声明成虚函数,并在主函数实现虚函数调用,输出点类、圆类和圆柱体类面积。提示:其他数据成员和...
  • CStatic派生类

    2019-08-26 11:18:55
    CStatic控件派生类,实现背景颜色,字体颜色、内容、对齐、粗细、斜体、大小、下划线等,链接,字体闪烁,控件边框设置
  • 以此雇员为基类,从中派生出教师,其中要求在教师中加入一个计算教师工资的方法,教师工资=基本工资(1000)+课时(月工作量)×30。以此雇员为基类,从中派生出实验员,其中要求在实验员中加入一个计算...
  • 主要介绍了C++中基类和派生类之间的转换,有助于深入理解C++面向对象程序设计,需要的朋友可以参考下
  • 基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,具体表现在以下几个方面,需要的朋友可以参考下
  • 主要介绍了结合.net框架在C#派生类中触发基类事件及实现接口事件,示例的事件编程中包括接口和类的继承等面向对象的基础知识,需要的朋友可以参考下
  • 主要介绍了C++编程中派生类的析构函数,析构函数平时一般使用较少,需要的朋友可以参考下
  • 派生类的定义

    2021-03-22 21:18:01
    基类与派生类 继承(inheritance)是面对对象程序设计的一个重要特性,是软件复用(software reuse)的一个重要形式。 继承允许在原有类的基础上创建新的类,新类可以从一个或多个原有类中继承数据成员和成员函数,...

    类的继承与派生

    基类与派生类

    继承(inheritance)是面对对象程序设计的一个重要特性,是软件复用(software reuse)的一个重要形式。

    继承允许在原有类的基础上创建新的类,新类可以从一个或多个原有类中继承数据成员和成员函数,并且可以重新定义或增加新的成员,从而形成类的层次。继承具有传递性,不仅支持系统的可重用性,而且还促进系统的可扩充性。

    类的对象是各自封闭的,如果没继承性机制,则类对象中数据,函数就会出现大量重复

    继承改变了传统程序设计对不再使用的数据类型和函数进行改写甚至重写的方法,克服了程序无法重复使用的缺点。

    通过继承,可吸收现有类的数据和函数来创建新类,并增添新的成员增强此类,这样可以节约程序开发的时间。

    在C++中,继承就是在一个已存在的类的基础上建立一个新的类。已存在的类称为基类(base class),又称为父类;新建立的类称为派生类(dervied class),又称为子类

    一个新类从已有的类那里获得其特性,这种现象称为类的继承

    另一方面,从已有的父类产生一个新的子类,称为类的派生。派生类继承了基类的所有数据成员和成员函数,具有基类的特性,派生类还可以对成员作必要的增加或调整,定义自己的新特性。

    一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的。

    派生类分为单级派生多级派生

    在这里插入图片描述
    一个派生类可以只从一个基类派生,称为单一继承(single inheritance),这是最常见的继承形式,如图所示,类B和类C都只从类A派生
    在这里插入图片描述
    一个派生类有两个及两个以上的基类,称为多重继承(multiple inheritance),如图所示,类C从类A和类B派生。
    在这里插入图片描述
    基类与派生类之间的关系为:

    (1)基类是对派生类的抽象,派生类是对基类的具体化。基类抽取了它与派生类的公共特征,而派生类通过增加信息将抽象的基类变为某种具体的类型。

    (2)派生类是基类的组合,可以把多重继承看作是多个单一继承的简单组合。

    派生类的定义

    定义派生类的一般形式为:

    class 派生类名 : 类派生列表 { //类体
    	成员列表
    };
    

    除了增加类派生列表外,派生类的定义与类定义并无区别。

    类派生列表(class derivation list)指定了一个或多个基类(base class),具有如下形式:

    访问标号 基类名
    

    类派生列表可以指定多个基类,中间用逗号(,)间隔,基类名必须是已定义的类名字。

    访问标号表示继承方式,可以是public(公有继承),protected(保护继承)或private(私有继承),继承方式决定了对继承成员的访问权限。如果未给出访问标号则默认为private(私有继承)。

    派生类的成员列表描述的是派生类自己新增加的数据成员和成员函数。

    派生类举例:

    #include<iostream>
    using namespace std;
    
    class Base { //基类
    	private:
    		int b_number;
    	public:
    		Base() { } //基类不带参数的构造函数
    		Base(int i) : b_number(i) { } //基类带参数的构造函数
    		int get_number() { return b_number; }
    		void print() { cout<<b_number<<endl; }
    };
    
    class Derived : public Base { //派生类
    	private:
    		int d_number; //派生类增加的数据成员
    	public:
    		Derived(int i,int j) : Base(i),d_number(j) { } //派生类构造函数
    		void print() { //派生类增加的成员函数
    			cout<<get_number()<<" "; //派生类继承的成员函数
    			cout<<d_number<<endl;
    		}
    };
    
    int main()
    {
    	Base a(2);
    	Derived b(3,4);
    	cout<<"a is ";
    	a.print(); //基类的print
    	cout<<"b is ";
    	b.print(); //派生类的print
    	cout<<"base part of b is ";
    	b.Base::print(); //基类的print
    	return 0;
    }
    

    派生类的构成

    派生类的说明:

    (1)用作基类的类必须是已定义的,其原因是显而易见的:每个派生类包含并且可以访问其基类的成员,为了使用这些成员,派生类必须知道它们是什么。这一规则说明不可能从类自身派生出一个类

    (2)如果需要声明(但并不实现)一个派生类,则只需要声明类名即可。

    (3)显然,在一个派生类中,其成员由两部分构成:一部分是从基类继承得到的,另一部分是自己定义的新成员,所有这些成员仍然分为public(公有),private(私有)和protected(保护)三种访问属性。

    (4)友元关系不能继承。一方面,基类的友元对派生类的成员没有特殊访问权限。另一方面,如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。

    (5)如果基类定义了静态成员,则整个继承层次中只有一个这样的成员。无论从基类派生出多少个派生类,每个静态成员只有一个实例

    (6)静态成员遵循常规访问控制:如果静态成员在基类中为私有的,则派生类不能访问它。如果该静态成员在基类是公有的,则基类可以访问它,派生类也可以访问它。

    (7)一般地,可以使用作用域运算符(::)也可以使用对象成员引用运算符(.)或指针成员引用运算符(->)访问静态成员。

    在这里插入图片描述
    在实际的编程中,设计一个派生类包括4个方面的工作:
    (1)从基类接收成员
    除了构造函数和析构函数,派生类会把基类全部的成员继承过来。这种继承是没有选择的,不能选择接收其中一部分成员,而舍弃另一部分成员

    (2)调整基类成员的访问
    派生类接收基类成员是程序员不能选择的,但是程序员可以对这些成员作出访问策略

    (3)修改基类成员
    可以在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员,就实现了修改基类成员功能的效果

    (4)在定义派生类时增加新的成员
    一般还应当自己定义派生类的构造函数和析构函数

    #include<iostream>
    using namespace std;
    
    class Base { //基类
    	private:
    		int b_number;
    	public:
    		Base( ){} //基类不带参数的构造函数
    		Base(int i) : b_number (i) { } //基类带参数的构造函数
    		int get_number( ) {return b_number;}
    		void print( ) {cout << b_number << endl;}
    };
    
    class Derived : public Base { //派生类
    	private:
    		int d_number; //派生类增加的数据成员
    	public:
    		Derived(int i,int j):Base(i),d_number(j){ };
    		//派生类构造函数
    		void print() { //派生类增加的成员函数
    			cout << get_number() << " "; //派生类继承的成员函数
    			cout << d_number << endl;
    		}
    };
    
    int main()
    {
    	Base a(2);
    	Derived b(3,4);
    	cout<<"a is ";
    	a.print();
    	cout<<"b is ";
    	b.print();
    	return 0;
    }
    
    展开全文
  • 修改CEdit右键菜单的派生类,如只修改,删除afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); };消息函数,屏蔽的话,就保留。当然也可以删除另外两个函数
  • 实验三 派生类与继承 班级123班 姓名朱广金 学号122536 一实验目的 1学习类的继承能够定义和使用类的继承关系 2学习派生类的声明与定义方法 3掌握类的定义和对象的声明 4熟悉公有派生和私有派生的访问特性 5掌握派生...
  • C++派生类的构成 派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分。从基类继承的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性。正是这些新增加的成员体现了派生类与...
  • C++派生类与继承(超详细)

    千次阅读 多人点赞 2020-05-13 22:59:09
    派生类的概念 1.为什么要使用继承 继承性也是程序设计中的一个非常有用的、有力的特性, 它可以让程序员在既有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类, 从而较好地解决了代码重用的问题。 2.派生...

    一.派生类的概念

    1.为什么要使用继承

    继承性也是程序设计中的一个非常有用的、有力的特性, 它可以让程序员在既有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类, 从而较好地解决了代码重用的问题。

    2.派生类的说明

    在类名 employee 的冒号之后, 跟着关键字 public 与类名 person,这就意味着类 employee 将继承类 person 的全部特性。
    关键字 public 指出派生的方式,告诉编译程序派生类employee从基类 person 公有派生。

    // 定义一个基类( person 类)
    class person
    {
    private :
        char name [10] ;
        int age;
        char sex;
    public:
    // …
    } ;
    // 定义一个派生类
    class employeepublic person
    {
        char department[20] ;
        float salary;
    public:
        // …
    } ;
    
    

    声明一个派生类的一般格式为:

    class 派生类名∶派生方式 基类名
    {
    // 派生类新增的数据成员和成员函数
    } ;
    

    派生方式”可以是关键字 private 或 public。如果使用了 private, 则称派生类从基类私有派生; 如果使用了 public,则称派生类从基类公有派生。派生方式可以缺省, 这时派生方式默认为private ,即私有派生。

    1. 公有派生

    class employeepublic person
    {
        // …
    };
    
    

    2. 私有派生

    class employeeprivate person
    {
    // …
    } ;
    

    这两种派生方式的特点如下:

    1. 无论哪种派生方式, 基类中的私有成员既不允许外部函数访问, 也不允许派生类中的成员函数访问,但是可以通过基类提供的公有成员函数访问。
    2. 公有派生与私有派生的不同点在于基类中的公有成员在派生类中的访问属性。

    公有派生时, 基类中的所有公有成员在派生类中也都是公有的。

    私有派生时, 基类中的所有公有成员只能成为派生类中的私有成员。

    下面我们分别讨论私有派生和公有派生的一些特性。

    1. 私有派生

    (1 ) 私有派生类对基类成员的访问
    由私有派生得到的派生类, 对它的基类的公有成员只能是私有继承。也就是说基类的所有公有成员都只能成为私有派生类的私有成员, 这些私有成员能够被派生类的成员函数访问,但是基类私有成员不能被派生类成员函数访问。

    # include <iostream>
    using namespace std;
    class base  // 声明一个基类
    {
        int x;
    public:
        void setx(int n)
        {
            x = n;
        }
        void showx ()
        {
            cout << x << endl;
        }
    } ;
    class derived: private base  // 声明一个私有派生类
    {
        int y;
    public:
        void setxy(int n, int m)
        {
            setx( n) ;
            y = m;
        }
        void showxy()
        {
            cout<< x << y << endl; // 非法
        }
    };
    int main()
    {
        derived obj;
        obj.setxy( 10,20) ;
        obj.showxy() ;
        return 0 ;
    }
    
    

    例中首先定义了一个类 base , 它有一个私有数据 x 和两个公有成员函数 setx ( ) 和showx( ) 。将类 base 作为基类,派生出一个类 derived。派生类 derived 除继承了基类的成员外,还有只属于自己的成员: 私有数据成员 y、公有函数成员 setxy( )和 showxy( )。派生方式关键字是 private, 所以这是一个私有派生。
    类 derived 私有继承了 base 的所有成员, 但它的成员函数并不能直接使用 base 的私有数据 x, 只能使用两个公有成员函数。所以在 derived 的成员函数 setxy ( ) 中引用 bas的公有成员 setx( )是合法的, 但在成员函数 showxy( ) 中直接引用 base 的私有成员 x 是法的。

    如果将例中函数 showxy( )改成如下形式:
    void showxy( ) {showx( ) ; cout < < y < < endl; }
    重新编译,程序将顺利通过。可见基类中的私有成员既不能被外部函数访问, 也不能被派生类成员函数访问,只能被基类自己的成员函数访问。因此, 我们在设计基类时, 总要为它的私有数据成员提供公有成员函数,以使派生类或外部函数可以间接使用这些数据成员。
    (2 ) 外部函数对私有派生类继承来的成员的访问
    私有派生时,基类的公有成员在派生类中都成为私有成员, 外部函数不能访问。下面的例子将验证外部函数对私有派生类继承来的成员的访问性。

    #include <iostream>
    using namespace std;
    class base
    {
        // 声明一个基类
        int x;
    public:
        void setx(int n)
        {
            x = n;
        }
        void showx ()
        {
            cout << x << endl;
        }
    };
    class derived: private base
    {
        // 声明一个私有派生类
        int y;
    public:
        void sety(int n)
        {
            y = n;
        }
        void showy()
        {
            cout << y << endl;
        }
    };
    int main()
    {
        derived obj;
        obj .setx(10) ; // 非法
        obj .sety(20) ; // 合法
        obj .showx() ;  // 非法
        obj .showy() ;  // 合法
        return 0 ;
    }
    
    

    由于是私有派生, 所以基类 base 的公有成员 setx ( ) 和 showx ( ) 被 derived 私有继 承后, 成为 derived 的私 有成员, 只能 被derived的成员函数访问, 不能被外界函数访问。在 main ( )函数中, 定义了派生类 derived的对象 obj, 由于 sety ( ) 和 showy ( ) 在类 derived 中是 公有函 数, 所 以对 obj .sety ( ) 和obj .showy( )的调用是没有问题的, 但是对 obj .setx ( )和obj .showx( ) 的调用是非法的, 因为这两个函数在类 derived 中已成为私有成员。
    需要注意的是:无论 setx( ) 和 showx( )如何被一些派生类继承, 它们仍然是 base 的公有成员,因此以下的调用是合法的:

    base base-obj;
    base-obj.setx (2);
    
    1. 公有派生
      在公有派生中,基类中的私有成员不允许外部函数和派生类中的成员函数直接访问,但是可以通过基类提供的公有成员函数访问。基类中的公有成员在派生类中仍是公有成员,外部函数和派生类中的成员函数可直接访问。
    #include <iostream>
    using namespace std;
    class base
    {
        // 声明一个基类
        int x;
    public:
        void setx(int n)
        {
            x = n;
        }
        void showx()
        {
            cout << x << endl;
        }
    };
    class derived: public base
    {
        // 声明一个公有派生类
        int y;
    public:
        void sety(int n)
        {
            y = n;
        }
        void showy()
        {
            cout << y << endl;
        }
    } ;
    int main ()
    {
        derived obj;
        obj.setx(10);  // 合法
        obj.sety(20);  // 合法
        obj.showx();   // 合法
        obj.showy();   // 合法
        return 0 ;
    }
    
    

    例中类 derived 从类 base 中公有派生, 所以类 base 中的两个公有成员函数 setx ( ) 和showx( )在派生类中仍是公有成员。因此, 它们可以被程序的其它部分访问。特别是它们可以合法地在 main( )中被调用。
    说明:

    1. 派生类以公有派生的方式继承了基类, 并不意味着派生类可以访问基类的私有成员。
    class base
    {
        int x;
    public:
        void setx (int n )
        {
            x = n;
        }
        void showx()
        {
            cout << x << endl;
        }
    } ;
    class derivedpublic base
    {
        int y;
    public:
        void sety(int n)
        {
            y = n;
        }
        void show-sum()
        {
            cout << x + y << endl; // 非法
        }
        void showy()
        {
            cout << y << endl;
        }
    } ;
    
    

    派生类 derived 企图访问基类 base 的私有成员 x, 但是这种企图是非法的,因为基类无论怎样被继承, 它的私有成员都针对该基类保持私有性。

    1. 在派生类中声明的名字支配基类中声明的同名的名字, 即如果在派生类的成员函数中直接使用该名字的话,则表示使用派生类中声明的名字
    class X
    {
    public:
        int f() ;
    };
    class Ypublic X
    {
    public:
        int f();
        int g() ;
    };
    void Y∷g()
    {
        f() ;   // 表示被调用的函数是 Y∷f( ), 而不是 X∷f( )
    }
    
    

    对于派生类的对象的引用,也有相同的结论, 例如:
    Y obj;
    obj .f( ) ; / / 被调用的函数是 Y∷f( )
    如果要使用基类中声明的名字,则应使用作用域运算符限定, 例如:
    obj .X∷f( ) ; / / 被调用的函数是 X∷f( )
    在这里插入图片描述

    保护成员的作用

    protected 说明符可以放在类声明的任何地方,通常将它放在私有成员声明之后, 公有成员声明之前。类声明的一般格式如下所示:

    class 类名
    {
        [private:]
        私有数据成员和成员函数
    protected:
        保护数据成员和成员函数
    public:
        公有数据成员和成员函数
    };
    
    

    保护成员可以被派生类的成员函数访问,但是对于外界是隐藏起来的, 外部函数不能访问它。因此,为了便于派生类的访问, 可以将基类私有成员中需要提供给派生类访问的成员定义为保护成员。
    下面的程序说明类的私有成员、公有成员与保护成员是如何被访问的。

    # include < iostream .h >
    class samp
    {
        int a;
    protected:
        int b;      // 定义变量 b 为保护成员
    public:
        int c;
        samp(int n, int m)
        {
            a = n;
            b = m;
        }
        int geta()
        {
            return a ;
        }
        int getb()
        {
            return b;
        }
    };
    int main()
    {
        samp obj(20, 30);
        obj.b = 99;     // 非法,类的保护成员不能被外部函数访问
        obj .c = 50;    // 合法,类的公有成员能被外部函数访问
        cout << obj .geta()  <"";    // 合法
        cout << obj .getb() <<""<< obj .c << endl;    // 合法
        return 0 ;
    }
    
    

    对象 obj 的数据成员b 不能被访问,这是因为 b 是保护成员, 而类的保护成员是不允许外部函数访问的。
    C + + 规定, 派生类对于保护成员的继承与公有成员的继承很相似, 也分为两种情况: 若为公有派生, 则基类中的保护成员在派生类中也为保护成员;若为私有派生, 则基类中的保护成员在派生类中成为私有成员。
    下面的程序说明保护成员以公有方式被继承后的访问特性。

    #include <iostream>
    using namespace std;
    class base
    {
    protected:
        int a, b;
    public:
        void setab(int n, int m)
        {
            a = n;
            b = m;
        }
    };
    class derive : public base
    {
        int c;
    public:
        void setc(int n)
        {
            c = n;
        }
        void showabc()
        {
            cout << a <<" "<< b <<" "<< c << endl;
        } // 允许派生类成员函数
        // 访问保护成员 a 和 b
    } ;
    int main ()
    {
        derive obj;
        obj .setab(2, 4);
        obj .setc(3) ;
        obj .showabc() ;
        return 0 ;
    }
    
    

    由于 a 和 b 是基类 base 的保护成员, 而且被派生类以公有方式继承,所以它们可以被派生类的成员函数访问。但是基类的保护成员 a 与 b 不能被外部函数访问。

    下面程序说明保护成员以私有方式被继承后的访问特性。

    #include <iostream>
    using namespace std;
    class base
    {
    protected:
        int a;
    public:
        void seta (int sa)
        {
            a = sa;
        }
    } ;
    class derive1: private base
    {
    protected:
        int b;
    public:
        void setb(int sb)
        {
            b = sb;
        }
    } ;
    class derive2: public derive1
    {
        int c;
    public:
        void setc(int sc)
        {
            c = sc ;
        }
        void show( )
        {
            cout << "a = "<< a << endl;      // 非法
            cout << "b = "<< b << endl;      // 合法
            cout << "c = "<< c << endl;      // 合法
        }
    } ;
    int main()
    {
        base op1;
        op1.seta(1);
        derive1 op2;
        op2.setb(2);
        derive2 op3;
        op3.setc(3);
        op3.show();
    }
    
    

    基类 base 中的保护成员 a 被其派生类 derive1 私有继承后成为私有成员, 所以不能被 derive1 的派生类 derive2中的成员函数访问。derive1 类中的保护成员 b,被其派生类 derive2 公有继承后仍是保护成员,所以可以被 derive2 中的成员函数 show( )访问。

    派生方式基类中的访问权限派生类中的访问权限
    public(公有派生)public、protect、privatepublic、protect、private
    protect(私有派生)public、protect、privateprivate、private、private

    在公有派生情况下, 基类中所有成员的访问特性在派生类中维持不变;在私有派生情况下, 基类中所有成员在派生类中成为私有成员。

    二.派生类的构造函数和析构函数

    1.派生类构造函数和析构函数的执行顺序

    通常情况下,当创建派生类对象时, 首先执行基类的构造函数, 随后再执行派生类的构造函数; 当撤消派生类对象时, 则先执行派生类的析构函数, 随后再执行基类的析构函数。
    下列程序的运行结果,反映了基类和派生类的构造函数及析构函数的执行顺序。

    #include<iostream>
    using namespace std;
    class base
    {
    public:
        base()
        {
            cout<< "Constructing base class \n";
        } // 基类的构造函数
        ~base()
        {
            cout <<"Destructing baes class \n";
        } // 基类的析构函数
    } ;
    class derive : public base
    {
    public:
        derive() // 派生类的构造函数
        {
            cout << "Constructing derived class \n";
        }
        ~derive() // 派生类的析构函数
        {
            cout<< "Destructing derived class \n";
        }
    };
    int main()
    {
        derive op;
        return 0 ;
    }
    
    

    程序运行结果如下:

    Constructing base class
    Constructing derived class
    Destructing derived class
    Destructing base class
    构造函数的调用严格地按照先调用基类的构造函数, 后调用派生类的构造函数的顺序执行。析构函数的调用顺序与构造函数的调用顺序正好相反,先调用派生类的析构函数, 后调用基类的析构函数。

    2.派生类构造函数和析构函数的构造规则

    当基类的构造函数没有参数,或没有显式定义构造函数时, 派生类可以不向基类传递参数,甚至可以不定义构造函数。
    派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。
    在 C + + 中,派生类构造函数的一般格式为:

    派生类构造函数名(参数表) :基类构造函数名( 参数表)
    {
    / /}
    

    其中基类构造函数的参数,通常来源于派生类构造函数的参数表, 也可以用常数值。
    下面的程序说明如何传递一个参数给派生类的构造函数和传递一个参数给基类的构造函数。

    #include<iostream>
    using namespace std;
    class base
    {
        int i;
    public:
        base(int n) // 基类的构造函数
        {
            cout << "Constructing base class \n";
            i = n;
        }
        ~base () // 基类的析构函数
        {
            cout << "Destructing base class \n";
        }
        void showi()
        {
            cout << i << endl;
        }
    };
    class derive : public base
    {
        int j;
    public:
        derive(int n, int m) : base (m) // 定义派生类构造函数时,
        {
            // 缀上基类的构造函数
            cout << "Constructing derived class"<< endl;
            j = n;
        }
        ~derive() // 派生类的析构函数
        {
            cout << "Destructing derived class"<< endl;
        }
        void showj()
        {
            cout << j << endl;
        }
    };
    int main()
    {
        derive obj(30, 40) ;
        obj.showi();
        obj.showj();
        return 0 ;
    }
    
    

    程序运行结果为:
    Constructing base class
    Constructing derived class
    40
    30
    Destructing derived class
    Destructing base class
    当派生类中含有对象成员时,其构造函数的一般形式为:

    派生类构造函数名(参数表) :基类构造函数名( 参数表),对象成员名 1 (参数表),,对象成员名 n (参数表)
    {
        // …
    }
    
    

    在定义派生类对象时,构造函数的执行顺序如下:

    • 基类的构造函数
    • 对象成员的构造函数
    • 派生类的构造函数

    撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。
    下面这个程序说明派生类构造函数和析构函数的执行顺序。

    #include<iostream>
    using namespace std;
    class base
    {
        int x;
    public:
        base(int i) // 基类的构造函数
        {
            x = i;
            cout << "Constructing base class \n";
        }
        ~base() // 基类的析构函数
        {
            cout << "Destructing base class \n";
        }
        void show()
        {
            cout << "x = "<< x << endl;
        }
    } ;
    class derived: public base
    {
        base d;
        // d 为基类对象,作为派生类的对象成员
    public:
        derived(int i) : base (i), d(i) // 派生类的构造函数, 缀上基类构造函数和
        {
            // 对象成员构造函数
            cout
                    << "Constructing derived class \n";
        }
        ~derived() // 派生类的析构函数
        { cout << "Destructing derived class \n"; }
    } ;
    int main ()
    {
        derived obj(5 );
        obj.show();
        return 0 ;
    }
    
    

    程序执行结果如下:
    Constructing base class
    Constructing base class
    Constructing derived class
    x = 5
    Destructing derived class
    Destructing base class
    Destructing base class
    说明:

    1. 当基类构造函数不带参数时, 派生类不一定需要定义构造函数, 然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数, 甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。
    2. 若基类使用缺省构造函数或不带参数的构造函数, 则在派生类中定义构造函数时可略去“∶基类构造函数名(参数表)”; 此时若派生类也不需要构造函数, 则可不定义构造函数。
    3. 如果派生类的基类也是一个派生类, 则每个派生类只需负责其直接基类的构造,依次上溯。
    4. 由于析构函数是不带参数的, 在派生类中是否要定义析构函数与它所属的基类无关,故基类的析构函数不会因为派生类没有析构函数而得不到执行, 它们各 自是独立的。

    三.多重继承

    前面我们介绍的派生类只有一个基类, 这种派生方法称为单基派生或单一继承。当一个派生类具有多个基类时,这种派生方法称为多基派生或多重继承。

    1.多重继承的声明

    一般形式如下:

    class 派生类名: 派生方式 1 基类名 1,,派生方式 n 基类名 n
    {
    // 派生类新增的数据成员和成员函数
    } ;
    
    

    冒号后面的部分称基类表,各基类之间用逗号分隔, 其中“派生方式 i”( i = 1, 2, …, n )规定了派生类从基类中按什么方式继承: private 或 public,缺省的派生方式是 private

    class z: public x, y
    {
        // 类 z 公有继承了类 x,私有继承了类 y 
        // …
    };
    class z: x, public y
    {
        // 类 z 私有继承了类 x,公有继承了类 y
        // …
    };
    class z: public x, public y
    {
        // 类 z 公有继承了类 x 和类 y
        // …
    };
    
    

    在多重继承中,公有派生和私有派生对于基类成员在派生类中的可访问性与单继承的规则相同。

    #include <iostream>
    using namespace std;
    class X
    {
        int a ;
    public:
        void setX(int x)
        {
            a = x;
        }
        void showX()
        {
            cout << "a = "<< a << endl;
        }
    };
    class Y
    {
        int b;
    public:
        void setY(int x)
        {
            b = x;
        }
        void showY()
        {
            cout << "b = "<< b << endl;
        }
    };
    class Z: public X, private Y
    {
        int c;
    public:
        void setZ(int x, int y)
        {
            c = x;
            setY(y) ;
        }
        void showZ()
        {
            showY();
            cout << "c = "<< c << endl;
        }
    };
    int main()
    {
        Z obj;
        obj .setX(3);
        obj .showX();
        //obj .setY(4);   // 错误
       // obj .showY();   // 错误
        obj .setZ(6, 8 );
        obj .showZ();
    }
    
    

    根据派生的有关规则,类 X 的公有成员在 Z 中仍是公有成员, 类 Y 的公有成员在 Z 中成为私有成员。所以, 在主函数中对类 X 的公有成员函数的引用是正确的, 因为在 Z 中它们仍是公有成员; 对 Y的成员函数的引用是错误的,因为 Y 的成员函数在 Z 中已成为私有成员,不能直接引用。
    删去标有错误的两条语句,程序运行结果如下:
    a = 3
    b = 8
    c = 6
    说明:对基类成员的访问必须是无二义的, 例如下列程序段对基类成员的访问是二义的,必须想法消除二义性。

    class X
    {
    public:
        int f();
    };
    class Y
    {
    public:
        int f();
        int g();
    };
    class Zpublic X, public Y
    {
    public:
        int g();
        int h();
    };
    
    

    假如定义类 Z 的对象 obj: Z obj;
    则以下对函数 f( )的访问是二义的:
    obj .f( ) ;

    • / / 二义性错误,不知调用的是类 X 的 f( ) ,还是类 Y 的 f( )

    使用成员名限定可以消除二义性,例如:
    obj .X∷f( ) ;

    • / / 调用类 X 的 f( )

    obj .Y∷f( ) ;

    • / / 调用类 Y 的 f( )

    2.多重继承的构造函数与析构函数

    多重继承构造函数的定义形式与单继承构造函数的定义形式相似, 只是 n 个基类的构造函数之间用“,”分隔。多重继承构造函数定义的一般形式如下:

    派生类构造函数名(参数表) :基类 1 构造函数名 ( 参数表), 基类 2 构造函数名 (参数表),,基类 n 构造函数名(参数表)
    {
        // …
    }
    
    

    例如,由一个硬件类 Hard 和一个软件类 Soft ,它们共同派生出系统类 System, 声明如下:

    class Hard
    {
    protected:
        char bodyname[20];
    public:
        Hard(char * bdnm );     // 基类 Hard 的构造函数
        // …
    };
    class Soft
    {
    protected:
        char os[10];
        char Lang[15];
    public:
        Soft( char * o, char * lg);// 基类 Soft 的构造函数
        // …
    } ;
    class System: public Hard, public Soft
    {
    private:
        char owner[10] ;
    public:
        System( char * ow, char * bn, char * o, char * lg) // 派生类 System 的构造函数Hard( bn), Soft(o, lg);
        // 缀上了基类 Hard 和 Soft 的构造函数
        // …
    };
    
    

    注意:在定义派生类 System 的构造函数时,缀上了 Hard 和 Soft 的构造函数。
    再如,现有一个窗口类 window 和一个滚动条类 scrollbar, 它们可以共同派生出一个带有滚动条的窗口,声明如下:

    class window
    {
        // 定义窗口类 window
        // …
    public:
        window(int top, int left, int bottom, int right);
        ~window();
        // …
    } ;
    class scrollbar
    {
        // 定义滚动条类 scrollbar
        // …
    public:
        scrollbar(int top, int left, int bottom, int right);
        ~scrollbar();
        // …
    };
    class scrollbarwind∶window,scrollbar
    {
        / / 定义派生类
        / /public:
        scrollbarwind(int top, int left, int bottom, int right);
        ~scrollbarwind();
        // …
    };
    scrollbarwind∷scrollbarwind(int top, int left, int bottom, int right)window( top, left,bot tom, right),scrollbar(top, right - 20, bottom, right)
    {
        // …
    }
    
    

    在这个例子中, 定义派生类 scrollbarwind 的构造函数时, 也缀上了基类 window 和scrollbar 的构造函数。
    下面我们再看一个程序,其中类 X 和类 Y 是基类, 类 Z 是类 X 和类 Y 共同派生出来的,请注意类 Z 的构造函数的定义方法。

    #include<iostream>
    using namespace std;
    class X
    {
        int a;
    public:
        X(int sa ) // 基类 X 的构造函数
        {
            a = sa;
        }
        int getX()
        {
            return a ;
        }
    };
    class Y
    {
        int b;
    public:
        Y(int sb) // 基类 Y 的构造函数
        {
            b = sb;
        }
        int getY()
        {
            return b;
        }
    } ;
    class Z: public X, private Y
    {
        // 类 Z 为基类 X 和基类 Y 共同的派生类
        int c;
    public:
        Z(int sa, int sb, int sc ) :X(sa), Y(sb) // 派生类 Z 的构造函数,缀上
        {
            c = sc ;
        } // 基类 X 和 Y 的构造函数
        int getZ()
        {
            return c;
        }
        int getY()
        {
            return Y::getY();
        }
    };
    int main()
    {
        Z obj( 2, 4, 6) ;
        int ma = obj.getX();
        cout << "a = "<< ma << endl;
        int mb = obj .getY();
        cout << "b = "<< mb << endl;
        int mc = obj .getZ();
        cout << "c = "<< mc << endl;
        return 0 ;
    }
    
    

    上述程序运行的结果如下:
    a = 2
    b = 4
    c = 6
    由于派生类 Z 是 X 公有派生出来的, 所以类 X 中的公有成员函数 getX( )在类 Z 中仍是公有的, 在 main ( ) 中可以直接引用, 把成员 a 的值赋给 main ( ) 中的变量 ma ,并显示在屏幕上。类 Z 是从类 Y 私有派生出来的, 所以类 Y 中的公有成员函数 getY( ) 在类 Z 中成为私有的,在 main( ) 中不能直接引用。为了能取出 b 的值,在 Z 中另外定义了一个公有成员函数 Z∷getY( ) ,它通过调用 Y∷getY( ) 取出 b 的值。主函数 main( )中的语句:int mb = obj .getY( ) ;调用的是派生类 Z 的成员函数 getY( ) , 而不是基类 Y 的成员函数 getY( )。由于类 Z 中的成员函数 getZ( ) 是公有成员,所以在 main( ) 中可以直接调用取出 c 的值。

    总结:
    多重继承的构造函数的执行顺序与单继承构造函数的执行顺序相同, 也是遵循先执行基类的构造函数,再执行对象成员的构造函数, 最后执行派生类构造函数的原则。在多个基类之间, 则严格按照派生类声明时从左到右的顺序来排列先后。而析构函数的执行顺序则刚好与构造函数的执行顺序相反。

    3.虚基类

    为什么要引入虚基类

    当引用派生类的成员时, 首先在派生类自身的作用域中寻找这个成员, 如果没有找到,则到它的基类中寻找。如果一个派生类是从多个基类派生出来的, 而这些基类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时, 可能会产生二义性。

    #include <iostream>
    using namespace std;
    class base
    {
    protected:
        int a;
    public:
        base()
        {
            a = 5;
        }
    };
    class base1:public base
    {
    public:
        base1()
        {
            cout << "base1 a = "<< a << endl;
        }
    };
    class base2:public base
    {
    public:
        base2()
        {
            cout << "base2 a = "<< a << endl;
        }
    };
    class derived: public base1, public base2
    {
    public:
        derived()
        {
            cout << "derived a = "<< a << endl;
        }
    };
    int main()
    {
        derived obj;
        return 0 ;
    }
    
    

    上述程序中,类 base 是一个基类, 从类 base 派生出类 base1 和类 base2, 这是两个单一继承;从类 base1 和类 base2 共同派生出类 derived, 这是一个多重继承。
    这是一个存在问题的程序, 问题出在派生类 derived 的构造函数的定义上, 它试图输出一个它有权访问的变量 a, 表面上看来这是合理的,但实际上它对 a 的访问存在二义性,即函数中的变量 a 的值可能是从 base1 的派生路径上来的, 也有可能是从类 base2 的派生路径上来的,这里没有明确的说明。
    虽然 base1 和 base2 是从同一个基类 base 派生而来的, 但它们所对应的是基类 base的不同拷贝。类 derived 是 base1 和 base2 的派生类,因此类 base 是类 derived 的间接基类,它有两个拷贝与类 derived 相对应,一个是 base1 派生路径上的拷贝,另一个是 base2 派生路径上的拷贝。当类 derived 要访问这个间接基类 base 时, 必须指定要 访问的是哪个路径上 的base 拷贝。
    为了解决这种二义性, C + + 引入了虚基类的概念。

    2.虚基类的概念

    不难理解,如果在上例中类 base 只存在一个拷贝, 那么对 a的引用就不会产生二义性。在 C + + 中,如果想使这个公共的基类只产生一个拷贝,则可以将这个基类说明为虚基类。这就要求从类 base 派生新类时, 使用关键字 virtual 将类base 说明为虚基类。

    #include <iostream>
    using namespace std;
    class base
    {
    protected:
        int a;
    public:
        base()
        {
            a = 5;
        }
    };
    class base1:virtual public base
    {
    public:
        base1()
        {
            cout << "base1 a = "<< a << endl;
        }
    };
    class base2:virtual public base
    {
    public:
        base2()
        {
            cout << "base2 a = "<< a << endl;
        }
    };
    class derived:public base1, public base2
    {
    public:
        derived()
        {
            cout << "derived a = "<< a << endl;
        }
    };
    int main ()
    {
        derived obj;
        return 0 ;
    }
    
    

    在上述程序中,从类 base 派生出类 base1 和类 base2 时,使用了关键字 virtual ,把类base 声明为 base1 和 base2 的虚基类。这样, 从 base1 和base2 派生出的类 derived 只有一个基类 base, 从而可以消除二义性。

    3.虚基类的初始化

    虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同。虚基类构造函数的调用顺序是这样规定的:

    1. 若同一层次中包含多个虚基类, 这些虚基类的构造函数按对它们说明的先后次序调用。
    2. 若虚基类由非虚基类派生而来, 则仍然先调用基类构造函数, 再调用派生类的构造函数。
    3. 若同一层次中同时包含虚基类和非虚基类, 应先调用虚基类的构造函数, 再调用非虚基类的构造函数,最后调用派生类构造函数, 例如:
    class Xpublic Y, virtual public Z
    {
        // …
    };
    X one;
    
    

    定义类 X 的对象 one 时,将产生如下的调用次序:
    Z( ) ;
    Y( ) ;
    X( ) ;

    #include<iostream>
    using namespace std;
    class base
    {
        int a ;
    public:
        base (int sa)
        {
            a = sa;
            cout << "Constructing base"<< endl;
        }
    };
    class base1: virtual public base
    {
        int b;
    public:
        base1 (int sa, int sb) : base(sa)
        {
            b = sb;
            cout << "Constructing baes1"<< endl;
        }
    };
    class base2: virtual public base
    {
        int c;
    public:
        base2 (int sa, int sc) : base (sa)
        {
            c = sc ;
            cout << "Constructing baes2"<< endl;
        }
    };
    class derived: public base1, public base2
    {
        int d;
    public:
        derived(int sa, int sb, int sc, int sd ) :
            base(sa ), base1 (sa,sb), base2(sa, sc )
        {
            d = sd;
            cout << "Constructing derived"<< endl;
        }
    };
    int main()
    {
        derived obj(2, 4, 6, 8 ) ;
        return 0 ;
    }
    
    

    在上述程序中, base 是一个虚基类,它只有一个带参数的构造函数, 因此要求在派生类 base1、base2 和 derived 的构造函数的初始化表中,都必须带有对 base 构造函数的调用。

    如果 base 不是虚基类,在派生类 derived 的构造函数的初始化表中调用 base 的构造函数是错误的,但是当 base 是虚基类且只有带参数的构造函数时, 就必须在类 derived 构造函数的初始化表中调用类 base 的构造函数。因此, 在 derived 构造函数的初始化表中,不仅含有对 base1 和 base2 构造函数的调用, 还有对虚基类 base 构造函数的调用。上述程序运行的结果为:
    Constructing base
    Constructing base1
    Constructing base2
    Constructing derived

    不难看出,上述程序中虚基类 base 的构造函数只执行了一次。显然, 当 derived 的构造函数调用了虚基类 base 的构造函数之后, 类 base1 和类 base2 对 base 构造函数的调用被忽略了。这也是初始化虚基类和初始化非虚基类不同的地方。
    说明:

    1. 关键字 virtual 与派生方式关键字 ( public 或 private ) 的先后顺序无关紧要, 它只说明是“虚拟派生”。例如以下两个虚拟派生的声明是等价的。
    class derived: virtual public base{
    // …
    } ;
    class derived: public virtual base{
    // …
    } ;
    
    1. 一个基类在作为某些派生类虚基类的同时, 又作为另一些派生类的非虚基类, 这种情况是允许存在的,例如:
    class B{
    // …
    } ;
    class Xvirtual public B{
    // …
    } ;
    class Yvirtual public B{
    // …
    } ;
    class Zpublic B{
    // …
    } ;
    class AApublic X, public Y , public Z{
    // …
    } ;
    

    此例中,派生类 AA 由类 X、类 Y 和类 Z 派生而来。AA 与它的间接基类 B 之间的对应关系是:类 B 既是 X、Y 继承路径上的一个虚基类, 也是 Z 继承路径上的一个非虚基类。

    展开全文
  • 自己找的几个例子,免费的,希望对绘图的亲们有帮助!
  • VC MFC CEDIT派生类 控件背景色 字体修改 并能自动控制显示行数
  • 《Visual C++2012入门经典(第6版)》实例,在派生类的构造函数中调用基类的构造函数
  • 基类,派生类

    2012-04-02 21:35:51
    基类,派生类
  • 派生类的构造与析构派生类的构造派生类的构造与派生类的构造与析构析构与析构派生类的构造与析构
  • 本文实例讲述了C#接口在派生类和外部类中的调用方法。分享给大家供大家参考,具体如下: C#的接口通过interface关键字进行创建,在接口中可以包含属性,方法等成员变量。接口的派生类可以对接口中的方法进行实现。一...
  • C++派生类

    2020-11-24 20:33:51
    C++派生类派生类的概念 派生类的概念 类之间有一种层次关系,有父亲类,孩子类。 父类(基类,超类)派生出来子类(派生类)。 继承:有父亲类,有孩子类,就构成了层次关系。继承是面向对象程序设计的核心思想之一...

    C++派生类

    派生类的概念

    类之间有一种层次关系,有父亲类,孩子类。
    父类(基类,超类)派生出来子类(派生类)。
    继承:有父亲类,有孩子类,就构成了层次关系。继承是面向对象程序设计的核心思想之一。
    这种继承,要先定义一个父类。父类中定义一些公用的成员变量,成员函数。通过继承父类构建新的类:子类。所以写代码是,只需要写和子类相关的东西即可。子类一般会比父类更加庞大。

    定义一个Human类:

    #pragma once
    #ifndef __HUMAN__
    #define __HUMAN__
    #include <iostream>
    class Human {
    	Human();
    	Human(int);
    public:
    	int m_Age;
    	char m_Name;
    
    };//类结尾一定要分号
    
    #endif // !__HUMAN__
    

    定义一个Humen的子类Men:

    #pragma once
    #ifndef __MAN__
    #define __MAN__
    #include <iostream>
    #include "Human.h"
    //成为Human的子类
    class Man : public Human //表示Men是Humen的子类
    {
    	Man();
    public:
    
    };//类结尾一定要分号
    
    #endif // !__HUMAN__
    

    class Men:public Human:表示Men是Humen的子类。
    class 子类名:继承方式 父类名
    继承方式(访问权限):public ,private,protected。

    展开全文
  • 【C++】C++继承和派生类、虚基类

    万次阅读 多人点赞 2018-06-07 18:46:17
    派生则是继承的直接产物,它通过继承已有的一个或多个来产生一个新的,通过派生可以创建一种类族。 继承 基本概念 在定义一个A时,若它使用了一个已定义B的部分或全部成员,则称A继承了...
  • C++ 继承性:派生类

    千次阅读 2018-05-17 00:05:47
    C++继承性:派生类 继承:继承是面向对象程序设计的一个重要机制,该机制自动地为一个类提供来自另一个类的操作和数据结构,这使程序员只需在新类中定义已有类中没有的成员来建立新类。即,它允许在既有类的基础...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 353,634
精华内容 141,453
关键字:

派生类