精华内容
下载资源
问答
  • 文章目录定义基类和派生类多基派生虚基类派生类的构造函数派生类的析构函数 定义基类和派生类 class 派生类名:派生方式 基类名 { private: 新增私有成员列表; public: 新增公开成员列表; }; 三种数据成员...

    定义基类和派生类

    class 派生类名:派生方式 基类名
    {
    	private:
    		新增私有成员列表;
    	public:
    		新增公开成员列表;
    };
    

    三种数据成员、三种派生方式及访问权限

    派生方式privateprotectedpublic
    基类成员privateprotectedpublicprivateprotectedpublicprivateprotectedpublic
    派生类成员不可见privateprivate不可见privateprotected不可见protectedpublic
    外部不可见不可见不可见不可见不可见不可见不可见不可见可见

    可以归纳出以下几条准则:

    1、基类的private成员在外部和其他类(包括派生类)中都是不可见的;

    2、private派生使得基类中的非private成员都成为派生类中的private成员,在外部和其他类中无法访问;

    3、protected派生使得基类中的非private成员都降一级:基类中的protected成员成为派生类中private成员,基类中的public成员成为派生类中的protected成员;

    4、public派生时,基类中的非private成员在派生类中的访问属性保持不变。

    多基派生

    派生类只有一个基类时,称为单基派生

    派生类同时具有多个基类,这种方法称为多基派生或多重继承

    在C++中,声明和定义具有两个以上基类的派生类与声明单基派生类的形式类似,只需将 要继承的多个基类用逗号分开即可。如下例当中,派生类C有两个基类,按继承的规则,类C中包含了基类A的成员、基类B中的成员以及该类本身的成员。

     class A
     {  
         //类定义
     };
     class B
     {  
         //类定义
     };
     class C: public A, public B
     {  
         //在A和B基础上添加的成员列表
     };
    

    二义性问题

    一般来说,在派生类中对基类成员的访问应当具有唯一性,但在多基继承时,如果多个基类中存在同名成员的情况,造成编译器无从判断具体要访问哪个基类中的成员,则称为对基类成员访问的二义性问题。

    解决方案:若两个基类中具有同名的数据成员或成员函数,应使用成员名限定来消除二义性。

    虚基类

    多基派生中,如果在多条继承路径上有一个共同的基类,即共同基类。

     class A
     {  
         //类定义
     };
     class B: virtual public A
     {  
     };
     class C: virtual public A
     {  
     };
     class D: public B, public C
     {  
         //类D由类B和类C派生而来,在D类对象再,会有来自两条不同路径的共同基类(类A)的双重复制;
     };
    

    共同基类和多基派生的共同作用,使得在派生类中会出现多个共同基类的复制,这很容易带来二义性问题。

    解决方案:使用关键字virtual 将共同基类A声明为虚基类。

    class 派生类名:virtual 派生方式 基类名
    {
    	//类定义
    };
    

    关于虚基类需要注意:

    1、关键字virtual 和派生方式的先后顺序无关;

    2、为保证共同基类的成员在派生类中只有一个备份,必须将共同基类的直接派生类(类B和类C)都定义为virtual 方式,否则,派生类从所有virtual 派生的路径中只能得到共同基类的一个备份,还会从其他每条非virtual 派生路径中得到一个备份。

    区分虚基类派生二义性和多基派生二义性:

    多基派生的二义性主要是成员名的二义性,通过加作用域限定符来解决;

    而虚基类派生二义性是共同基类成员的多重复制带来的存储二义性,通过加virtual派生来解决。

    派生类的构造函数

    派生时,构造函数和析构函数都是不能继承的,对派生类必须重新定义构造函数和析构函数。

    由于派生类对象中包含了基类数据成员的值,因此创建派生类对象时,系统首先通过派生类的构造函数来调用基类的构造函数,完成基类成员的初始化,而后对派生类中新增的成员进行初始化。

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

    派生类构造函数实现的功能,调用顺序如下:

    1、完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成;

    2、调用基类的构造函数完成基类成员的初始化;

    3、若派生类中含对象成员、const成员或引用成员,则必须在初始化表中完成其初始化;

    4、派生类构造函数体执行

    派生类的构造函数调用的是基类的构造函数,初始化表达式中是构造函数表达式,而对象成员在初始化时,初始化表达式中是对象名及初始化参数。

    派生类的析构函数

    析构函数同样不能继承,因此在执行派生类析构函数时,基类析构函数会被自动调用。

    执行顺序:先执行派生类的析构函数,再执行基类的析构函数,和执行构造函数的顺序正好相反。

    展开全文
  • 继承与虚基类

    2021-04-25 14:53:54
    声明基类的派生类与声明单基派生类的形式相似,只需将要继承的基类使用逗号分隔即可。 已经声明了类X和类Y,可以声明多重继承的派生类Z. class:public X,private Y{ //类z公有继承了类x,私有继承了类y 派生...

    一、多继承
    1.声明多继承派生类的方法
    声明多个基类的派生类与声明单基派生类的形式相似,只需将要继承的多个基类使用逗号分隔即可。
    已经声明了类X和类Y,可以声明多重继承的派生类Z.
    class:public X,private Y{ //类z公有继承了类x,私有继承了类y
    派生类z中新增的数据成员和成员函数
    };
    声明多继承派生类的一般形式如下:
    class 派生类名:继承方式1 基类名1,… ,继承方式i 基类名,…,继承方式n 基类名n
    {
    派生类新增的数据成员和成员函数
    };
    冒号后面的部分称基表类,各基类之间用逗号分隔,其中“继承方式i”(i=1,2,…,n)规定了派生类从基类中按什么方式继承:private、protected或public。默认的继承方式是private。

    #include<iostream>
    using namespace std;
    class A{   //声明基类A 
    	public:
    		void setA(int x)
    		{  a=x;}
    		void printA()
    		{  cout<<"a="<<a<<endl; } 
    	private:
    		int a;
    };
    class B{    //声明基类B 
    	public:
    		void setB(int x)
    	    { b=x;}
    	    void printB()
    	   	{  cout<<"b="<<b<<endl; } 
    	private:
    		int b;
    };
    class C:public A,private B{  //声明派生类C,公有继承了类A,私有继承了类B 
    	public:
    		void setC(int x,int y)
    		{
    			c=x;
    			setB(y);
    		}
    		void printC()
    		{
    			printB();
    			cout<<"c="<<c<<endl;
    		}
    	private:
    		int c;
    };
    int main()
    {
    	C obj;
    	obj.setA(11);  //正确,成员setA在类C中为公有成员 
    	obj.printA();  //正确,printA也为公有成员 
    	//obj.setB(33);  //错误,setB在类C中为私有成员 
    	//obj.printB();  //错误同上 
    	obj.setC(55,88);   //正确,setC为公有成员 
    	obj.printC();     //正确,同上 
    	return 0; 
    }
    

    2.二义性
    对基类成员的访问必须是无二义性的

    class X{
    	public:
    		int f();
    }; 
    class Y{
    	public:
    		int f();
    		int g();
    };
    class Z:public 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() 
    

    3.多继承派生类的构造函数与析构函数
    多重继承派生类的构造函数 的定义形式与单继承时的构造函数定义形式相似,只是在初始表中包含多个基类构造函数。这多个基类的构造函数之间用“,”分隔

    #include<iostream>
    using namespace std;
    class Base1{    //声明基类Base1 
    	public:
    		Base1(int sx)
    		{x=sx;}
    		int getx()
    		{return x;} 
    	private:
    		int x;
    };	
    class Base2{   //声明基类Base2 
    	public:
    		Base2(int sy)
    		{y=sy;}
    		int gety()
    		{return y;}
    	private:
    		int y;
    };
    class Derived:public Base1,private Base2{  //声明类Derived为基类Base1和Base2共同的派生类 
    	public:
    	    Derived(int sx,int sy,int sz):Base1(sx),Base2(sy)  //派生类的构造函数缀上对两个基类构造函数的调用 
    	    { z=sz;}
    	    int getz()
    	    { return z;}
    	    int gety()
    	    { return Base2::gety();}
    	private:
    		int z;
    };
    int main()
    {
    	Derived obj(1,3,5);
    	cout<<"x="<<obj.getx()<<endl;
    	cout<<"y="<<obj.gety()<<endl;
    	cout<<"z="<<obj.getz()<<endl;
    	return 0;
    }
    

    结果如图所示在这里插入图片描述
    由于析构函数是不带参数的,在派生类中是否要定义析构函数与它所属的基类无关,所以与单继承情况类似,基类的析构函数不会因为派生类没有析构函数而得不到执行,他们各自是独立的。析构函数的调用与构造函数的调用顺序相反。
    二、虚基类
    1.虚基类的作用
    如果一个类有多个直接基类,而这些直接基类又有一个共同的基类,则在最低层的派生类中会保留这个间接的共同基类数据成员的多份同名成员。在访问这些同名成员时,必须在派生类对象名后增加直接基类名,使其唯一地标识一个成员,以免产生二义性。

    #include<iostream>
    using namespace std;
    class Base{  //声明类Base1和Base2共同的基类Base 
    	public:
    		Base()
    		{
    			a=5;
    			cout<<"Base a="<<a<<endl;
    		}
    	protected:
    		int a;
    };
    class Base1:public Base{  //声明Base1是Base的派生类 
    	public:
    		Base1()
    		{
    			a=a+10;
    			cout<<"Base1 a="<<a<<endl;  //这是类Base1的a,即Base1::a 
    		}
    };
    class Base2:public Base{   //声明Base2是Base的派生类 
    	public:
    		Base2()
    		{
    			a=a+20;
    			cout<<"Base2 a="<<a<<endl;   //这是类Base2的a,即Base2::a 
    		}
    };
    class Derived:public Base1,public Base2{  //Derived是Base1和Base2的共同派生类,是Base的间接派生类 
    	public:
    		Derived()
    		{
    			cout<<"Base1::a="<<Base1::a<<endl;
    			cout<<"Base2::a="<<Base2::a<<endl;   //防止二义性 
    		}
    };
    int main()
    {
    	Derived obj;
    	return 0;
    }
    

    在这里插入图片描述
    虽然在类Base1和类Base2中没有定义数据成员a,但是它们分别从类Base继承了数据成员a,这样在类Base1和类Base2中同时存在着同名的数据成员a,他们都是类Base成员的拷贝。
    在这里插入图片描述
    由于在类Derived中同时存在着Base1和Base2的成员a,因此在Derived的构造函数中输出a的值必须加上“类名::”,否则会出现二义性。
    2.虚基类的声明

    #include<iostream>
    using namespace std;
    class Base{    //声明基类Base 
    	public:
    		Base()
    		{
    			a=5;
    			cout<<"Base a="<<a<<endl;
    		}
    	protected:
    		int a;
    };
    class Base1:virtual public Base{  //声明类Base是Base1的虚基类 
    	public:
    		Base1()
    		{
    			a=a+10;
    			cout<<"Base1 a="<<a<<endl; 
    		}
    };
    class Base2:virtual public Base{   //声明类Base是Base2的虚基类 
    	public:
    		Base2()
    		{
    			a=a+20;
    			cout<<"Base2 a="<<a<<endl;   
    		}
    };
    class Derived:public Base1,public Base2{    //类Derived是类Base1和类Base2的共同派生类,是类Base的间接派生类 
    	public:
    		Derived()
    		{
    		cout<<"Derived a= "<<a<<endl;
    		}
    };
    int main()
    {
    	Derived obj;
    	return 0;
    }
    

    程序运行结果如图所示:
    在这里插入图片描述
    在上述程序中,从类Base派生出类Base1和类Base2时,使用了关键字virtual,把类Base声明为Base1和Base2的虚基类。这样从类Base1和Base2派生出的类Derived只继承基类Base一次,也就是说,基类Base的数据成员只保留一份
    在这里插入图片描述
    3.虚基类的初始化
    虚基类的初始化与一般的多继承的初始化在语法上是一样的,但构造函数的调用顺序不同,在使用虚基类机制时应该注意以下几点:
    (1)如果在虚基类中定义有带形参的构造函数,并且没有定义默认形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化列表中列出对虚基类构造函数的调用,以初始化在虚基类中定义的数据成员。
    (2)建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。该派生类的其他基类对虚基类前造函数的调用都自动被忽略。
    (3)若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数。
    (4)对于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下。
    (5)对于非虚基类,构造函数的执行顺序仍是先左后右,自上而下。
    (6)若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。

    #include<iostream>
    using namespace std;
    class Base{    //声明基类Base 
    	public:
    		Base(int sa)
    		{
    			a=sa;
    			cout<<"Constructing Base"<<endl;
    		}
    	private:
    		int a;
    };
    class Base1:virtual public Base{   //声明Base是Base1的虚基类 
    	public:
    		Base1(int sa,int sb):Base(sa)
    		{
    			b=sb;
    			cout<<"Constructing Base1"<<endl;
    		}
    	private:
    		int b;
    };
    class Base2:virtual public Base{   //声明Base是Base2的虚基类 
    	public:
    		Base2(int sa,int sc):Base(sa)
    		{
    			c=sc;
    			cout<<"Constructing Base2"<<endl;
    		}
    	private:
    		int c;
    };
    class Derived:public Base1,public Base2
    {
    	public:
    	    Derived(int sa,int sb,int sc,int sd):Base(sa),Base1(sa,sb),Base2(sa,sc)
    		{
    			d=sd;
    		    cout<<"Constructing Derived"<<endl;
    		 } 
        private:
        	int d;
     };
    int main()
    {
    	Derived obj(2,4,6,8);
    	return 0;
    }
    

    程序运行结果如下:
    在这里插入图片描述
    如果类Base不是虚基类,在派生类Derived的构造函数的初始化列表中调用类Base的构造函数是错误的,但当类Base是虚基类且只带有参数的构造函数时,就必须在类Derived的构造函数的初始化列表中调用类Base的构造函数。

    展开全文
  • 最近在开发微信代扣,涉及大量HTTP调用微信,就写了一个泛型模板,将序列化,反序列化,HTTP调用全部整合至泛型基类中.public abstract class WxCallbackHandler implements CallbackHandler {private static final org....

    最近在开发微信代扣,涉及大量HTTP调用微信,就写了一个泛型模板,将序列化,反序列化,HTTP调用全部整合至泛型基类中.

    public abstract class WxCallbackHandler implements CallbackHandler {

    private static final org.slf4j.Logger LOG= LoggerFactory.getLogger(WxCallbackHandler.class);

    private static final String XML_TAG="xml";

    protected ThreadLocal context=new ThreadLocal();

    private static final Charset UTF8_CHARSET=Charset.forName("utf-8");

    private XStream xStream;

    private static XStream wxCommonResultXStream;

    static {

    wxCommonResultXStream = NoNameCoderWithCDataFacotry.Instance().create();

    wxCommonResultXStream.autodetectAnnotations(Boolean.TRUE);

    wxCommonResultXStream.alias(XML_TAG,WeixinXmlResult.class);

    }

    public WxCallbackHandler(){

    xStream = NoNameCoderWithCDataFacotry.Instance().create();

    xStream.autodetectAnnotations(Boolean.TRUE);

    //-----------------------------------------------------------------以下为获取泛型参数-----------------------------

    Class current=this.getClass();

    while(!WxCallbackHandler.class.equals(current.getSuperclass())){

    current=current.getSuperclass();/*为了处理多级继承*/

    }

    Type genType = current.getGenericSuperclass();

    Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

    //=============================================获取泛型参数结束=======================

    xStream.alias(XML_TAG, (Class) params[0]);

    }

    private void parser(CallbackParameter parameter){

    ServletInputStream inputStream=null;

    try {

    inputStream=parameter.getRequest().getInputStream();

    String xmlText = IOUtils.toString(inputStream, UTF8_CHARSET);

    if (LOG.isInfoEnabled()) {

    LOG.info("微信回调请求报文如下{}{}", parameter.toString(), xmlText);

    }

    context.set((C) xStream.fromXML(xmlText));

    }catch (Throwable throwable){

    LOG.error(throwable.getMessage(),throwable);

    throw new RuntimeException(throwable);

    }finally {

    IOUtils.closeQuietly(inputStream);

    }

    }

    @Override

    public void execute(CallbackParameter parameter) {

    parser(parameter);

    WeixinXmlResult result=new WeixinXmlResult(doExecute());

    if(LOG.isInfoEnabled()){

    LOG.info("回复微信调用结果如下{}", GsonUtils.defaultGson().toJson(result));

    }

    PrintWriter writer=null;

    try {

    HttpServletResponse response = parameter.getResponse();

    writer = response.getWriter();

    writer.print(wxCommonResultXStream.toXML(result));

    writer.flush();

    }catch (Throwable throwable){

    LOG.error(throwable.getMessage(),throwable);

    throw new RuntimeException(throwable);

    }

    finally {

    IOUtils.closeQuietly(writer);

    }

    }

    public abstract boolean doExecute();

    }

    展开全文
  • 一个类可以从基类派生。在多重继承模型中(其中的类派生自基类),则使用“基类列表”语法元素指定基类。比如: class Collection { }; class Book {}; class CollectionOfBook : public Book, public ...

    一个类可以从多个基类派生。在多重继承模型中(其中的类派生自多个基类),则使用“基类列表”语法元素指定基类。比如:

    class Collection {
    };
    class Book {};
    class CollectionOfBook : public Book, public Collection {
        // New members
    };
    

    指定基类的顺序并不重要,只不过在某些情况下,将调用析构函数和构造函数。这些情况下,指定基类的顺序将影响:

    • 构造函数进行初始化的顺序。如果你的代码依赖要在 Book 部分之前初始化的 CollectionOfBook 的 Collection 部分,则规范的顺序很重要。 初始化按在 基础列表 中指定类的顺序进行。
    • 调用析构函数以进行清理的顺序。同样,如果在销毁另一部分时必须呈现类的特定“部分”,则顺序非常重要。 析构函数的调用顺序与在 基础列表 中指定的类的顺序相反。

    注意:

    • 基类的规范顺序将会影响类的内存布局,不要基于内存中基成员的顺序做出任何编程决策
    • 指定基础列表时,不能多次指定类的内存布局。但是,可以将类多次作为派生类的间接基

    名称多义性

    • 多重继承使得沿着多个路径继承名称称为可能。沿着这些路径的类成员名称不一定是唯一的。这种名称冲突叫做多义性

    • 任何引用类成员的表达式必须采用明确的引用。

    以下示例说明如何产生多义性:

    class A {
    public:
        unsigned a;
        unsigned b();
    };
    
    class B {
    public:
        unsigned a();  // Note that class A also has a member "a"
        int b();       //  and a member "b".
        char c;
    };
    
    // Define class C as derived from A and B.
    class C : public A, public B {};
    

    对于前面的类声明,如下所示的代码是不明确的,因为 b 所指的 b 是在 A 中还是在 B 中并不清楚:

    C *pc = new C;
    
    pc->b();
    

    请看前面的示例。 由于名称 a 是类 A 和类 B 的成员,因此编译器无法辩明哪个 a 指定将调用函数。 如果成员可以引用多个函数、对象、类型或枚举数,则对该成员的访问是不明确的。
    编译器通过按此顺序执行测试来检测多义性:

    • 如果对名称的访问是不明确的(如上所述),则会生成错误消息。
    • 如果重载函数是明确的,则将解析它们。
    • 如果对名称的访问违背了成员访问权限,则会生成错误消息。

    在表达式通过继承产生多义性时,您可以通过限定考虑中的名称及其类名来手动消除该多义性。 若要适当编译上面的示例而不产生多义性,请使用如下代码:

    C *pc = new C;
    
    pc->B::a();
    

    虚拟基类

    • 由于一个类可能多次成为派生类的间接基类,因此C++提供了一种优化这种基类的工作方式的方法。虚拟基类提供了一种节省空间和避免多次使用多重继承的类层次结构出现多义性的方法。

    • 每个非虚拟对象包含在基类中定义的数据成员的一个副本。这种重复浪费了空间,并要求您在每次访问基类成员时必须指定所需基类成员的副本。

    • 当将某个类指定为虚拟基类时,该基类可以多次作为间接基类而无需复制其成员。基类的数据成员的单个副本由将其作为虚拟基类的所有基类共享

    • 声明虚拟基类时,vritual关键字显示在派生类的基类基列表中。

    请考虑下图中的类层次结构,它演示了模拟的午餐排队。
    在这里插入图片描述
    在该图中,Queue 是 CashierQueue 和 LunchQueue 的基类。 但是,当将这两个类组合成 LunchCashierQueue 时,会出现以下问题:新类包含类型 Queue 的两个子对象,一个来自 CashierQueue,另一个来自 LunchQueue。 下图显示了概念上的内存布局(实际物理内存布局可能会进行优化)。

    在这里插入图片描述
    请注意,Queue 对象中有两个 LunchCashierQueue 子对象。 以下代码将 Queue 声明为虚拟基类:

    class Queue {};
    class CashierQueue : virtual public Queue {};
    class LunchQueue : virtual public Queue {};
    class LunchCashierQueue : public LunchQueue, public CashierQueue {};
    

    virtual 关键字可确保仅包含子对象的一个副本 Queue (参阅下图) 。
    在这里插入图片描述

    • 一个类可能有一个特定类型的虚拟组件和非虚拟组件
      在这里插入图片描述
      在图中,CashierQueue 和 LunchQueue 将 Queue 用作虚拟基类。 但是,TakeoutQueue 将 Queue 指定为基类而不是虚拟基类。 因此,LunchTakeoutCashierQueue 具有类型 Queue 的两个子对象:一个来自包含 LunchCashierQueue 的继承路径,另一个来自包含 TakeoutQueue 的路径。 下图对此进行了演示。
      在这里插入图片描述
      与非虚拟继承相比较,虚拟继承提供了显著的大小优势。但是,它可能会引入额外的处理开销:
    • 使用virtual继承而来的类所产生的对象往往比使用non-virtual继承的兄弟体积大,访问虚基类的成员函数时,也比如non-virtual base classs更慢
    • 支配“virtual bases初始化”的规则比non-virtual base的情况更复杂。virtual bases的初始化责任是由继承体系中的最底层类负责,这暗示:
      • 类如果派生自virtual bases而需要初始化,必须认知其virtual bases—不管那些bases有多远
      • 当一个新的派生类加入继承体系中,它必须承担virtual bases(不论直接或者间接)的初始化责任

    建议

    • 非必要不使用virtual base。平时请使用non-virtual继承
    • 如果你必须使用virtual base classes,尽可能避免在其中放置数据。

    示例

    下面是一个用来塑模“人”的C++ Interface class:

    class IPerson{     // 这个类需要指出实现接口
    public:
    	virtual ~IPerson();
    	virtual std::string name() const = 0;
    	virtual std::string birthDate() const = 0;
    };
    
    
    

    IPerson的客户必须以IPerson的指针或者引用来编写程序,因此抽象类无法被实体化创建对象。为了创建一些可以被当作IPerson来使用的对象,IPerson的客户使用factoryfunction将“派生自IPerson的具现类”实体化:

    std::shared_ptr<IPerson> makePerson(DatebaseID perIdentifier);
    
    DatebaseID askUserForDatabaseID();
    
    DatebaseID id(askUserForDatabaseID());
    std::shared_ptr<IPerson> pp(makePerson(id));
    

    但是makePerson如何创建对象并返回一个指针指向它呢?五一一定有些派生自IPerson的局限class,在makePerson中可以创建。

    假设这个类叫做CPerson。就像具象class一样,CPerson必须提供“继承自IPerson”的纯虚函数的实现代码。我们可以从无到有写出这些东西,但是更好的是利用既有组件。后者做了大部分必要的事情。比如,假设有个既有的数据库类,名为PersonInfo,提供CPerson所需要的实质东西:

    class PersonInfo{
    public:
    	explicit PersonInfo(DatebaseID  pid);
    	virtual ~PersonInfo();
    	virtual const char * theName() const;
    	virtual const char * theBirthDate() const;
    private:
    	virtual const char *valueDelimOpen()const;
    	virtual const char *valueDelimClose()const;
    }
    
    const char * PersonInfo::valueDelimOpen()const{
    	return "[";
    }
    
    const char *PersonInfo::valueDelimClose()const{
    	return "]";
    }
    
    const char *PersonInfo::theName()const{
    	// 保留缓冲区给返回值只用:因为缓冲区是static,因此会被自动初始化为“全部是0”;
    	static char value[MAX_LNGTH];  // 不好的设计:会带来超限问题和线程问题
    
    	std::strcpy(value, valueDelimOpen());
    	// ... 
     	std::strcat(value, valueDelimClose());
     
    	return value;
    }
    

    因为CPerson的name和birthDate必须返回类型“xxx”而不是”[xxx]“, 所以必须重新实现valueDelimClose和valueDelimOpen

    CPerson和PersonInfo的关系是, PersonInfo刚好由若干函数可以帮助CPerson比较容易实现出来,它们的关系是根据某物实现出。而这种关系可以使用复合或者private继承实现。一般来讲复合更好,但如果需要重新定义虚函数,就必须继承。因此这里必须private继承才能满足要求(CPerson也可以结合复合+继承技术以有效定义PersonInfo的虚函数)。

    但CPerson也必须实现IPerson,业就是说必须public继承才能完成。因此这里需要多重继承:

    class IPerson{     // 这个类需要指出实现接口
    public:
    	virtual ~IPerson();
    	virtual std::string name() const = 0;
    	virtual std::string birthDate() const = 0;
    };
    
    class PersonInfo{
    public:
    	explicit PersonInfo(DatebaseID  pid);
    	virtual ~PersonInfo();
    	virtual const char * theName() const;
    	virtual const char * theBirthDate() const;
    private:
    	virtual const char *valueDelimOpen()const {return "["; };
    	virtual const char *valueDelimClose()const {return "]"; };
    }
    
    
    class CPerson : public IPerson, private PersonInfo{
    public:
    	explicit CPerson(DatebaseID  pid) : PersonInfo(pid) {}
    	virtual const char * theName() const {return PersonInfo::theName(); }
    	virtual const char * theBirthDate() const {return PersonInfo::theBirthDate(); }
    
    private:
    	virtual const char *valueDelimOpen()const {return ""; };
    	virtual const char *valueDelimClose()const {return ""; };
    	
    }
    

    在这里插入图片描述

    注意

    • 多重继承比单一继承复杂。它可能导致新的歧义性,以及对vritual继承的需要
    • virtual继承会增加大小、速度、初始化、赋值等成本。如果virtual base classes不带任何数据,将是最实用价值的情况
    • 多重继承的确由正当用途。其中一个情节涉及“public继承某个interface class”和“private继承某个协助实现的类”的两相结合
    展开全文
  • I know that we can use interfaces to inherit from multiple classes but is it possible to inherit the state as well?How can I inherit methods with definitions from 2 classes and have them in a third cl...
  • //虚方法、虚基类基类指针,指向派生类的指针只能指向所指派生类重写的方法,以及指针所属类的//所有公有方法#include using std::cout;using std::endl;class Base{public:virtual void fun(){cout << ...
  • * 从Person类派生出学生类Student和教师类Teacher; * 从Student类中派生...根据类视图完成类的定义以及相应的构造函数,注意虚基类的使用。在main函数中创建类的对象测试这些类。 下面给出各个类的成员和继承关系。
  • 选择基类的访问属性你现在知道在定义类的访间属性时可用的选择项,你希望使用这些类定义子类。你知道在类继承上这些属性所具有的效果,但是你如何决定到底应该使用哪一个呢?这里没有死板和现成的规则,你选择的...
  • C++ 线程基类

    千次阅读 2021-06-05 11:11:28
    这里简单实现了一个线程基类,其中未包括线程关闭的方法,等待后续添加。 XThread.h #pragma once #include <thread> class XThread { public: XThread() = default; virtual ~XThread() = default; ...
  • C++ 抽象基类

    2021-04-29 00:20:38
    C++ 抽象基类 一、简介 抽象基类: abstract base class,简称ABC,只定义接口,而不涉及实现,主要用于定义派生类的通用接口。抽象基类必 须包含至少一个纯虚方法。且包含纯虚函数的抽象基类,不能用来创建对象,...
  • python之抽象基类

    2021-02-03 02:35:58
    python之抽象基类抽象基类,在这个类中定义一些方法,所有继承这个类的类必须实现这个方法,并且这个类不能被实例化,使用抽象基类的情况:1.某些情况下希望判断某个对象的类型2.强制子类必须实现某些方法在python...
  • 单例模式 基类泛型

    2021-03-09 07:37:09
    请百度,因为他在这篇文章中不是重点) 有了这些知识点有没有想把他封装成一个基类 ,只要继承他就实现了单例模式,这就是我说的不一样的风格 public abstract class Singleton where TEntity:class{ private static...
  • # 模型基类之模型基类的用途[TOC]>[danger] model的基类的功能相对控制...[info] 除了每张表建立一个物理模型 你也可以建立服务和逻辑层既然model重要,那我我们把更的复用的代码封装在基类中 增加开发速度同时...
  • python 基类是什么意思

    2021-04-26 19:15:16
    这里的父类指的是被继承的类,也叫做基类;子类指的是继承其它类的类,也叫做派生类基于至少两个类之间才有继承,比如B类继承A类,那么A就是B的父类(又叫超类、基类)。2、基类的查看Python 为所有类都提供了一个 ...
  • 基类成员被派生类继承以后访问权限保持不更相关问题在商业洽谈中可以用你的热情来感染对方。()在商业计划书中,战略规划应该分阶段和分年份提出()。在商代有学者认为,( )与金(青铜)、玉一样,都是专门的礼用材料...
  • js 抽象基类

    2021-03-24 11:15:43
    1. 抽象基类 有时候可能需要定义这样一个类,它可供其他类继承,但本身不会被实例化。虽然 ECMAScript 没 有专门支持这种类的语法 ,但通过 new.target 也很容易实现。new.target 保存通过 new 关键字调用的类或...
  • 前段时间一直在学习C++中对象的内存布局,由于C++中支持继承和虚继承,使得对象的内存布局可能变得有些复杂,刚开始去学习时会有点摸不着头脑。另外不同的编译器很可能有着不同的内存布局,进一步加大了学习难度。...
  • 一、Object类的疑问Object是所有java类型的基类,但是随便定义一个类型Test类,它是如何继承于Object的呢?public classTest{public static voidmain(String[] args){System.out.println("这是一个测试类");}}这无非...
  • Vehicle基类

    2021-01-28 10:03:24
    定义一个车 (Vehicle) 基类,有 run、stop 等成员函数,由此派生出自行车 (Bicycle) 类、汽车 (Motorcar) 类,从 Bicycle 和 Motorcar 派生出摩托车(Motorcycle)类,它们都有run、stop 等成员函数。观察虚函数的作用...
  • 本文旨意封装在MVP模式中的基类如Activity,Fragment,Presenter类。以下内容建议在了解了mvp模式的读者阅读,如果还有对mvp架构模式有疑问的,请看我的另外一篇文章《Android MVP架构模式初窥门径》为什么需要封装...
  • 这里为什么我们可以认为delete一个基类指针(基类是没有析构函数),不会照成内存泄漏呢?这就是C++的new 和 delete 的特有机制和职责了.下面看这句话: “…当在堆栈里主动创建对象时,对象的大小和它们的声明周期被...
  • 是的,你可以做到.在包a中定义A:package a;public class A {void method1() {System.out.println("A1");}public void method2() {method1();System.out.println("A2");}}在包b中定义B:package b;...
  • 一个实体类基类实现

    2021-02-28 15:37:27
    /** * 重写toString()的实体类基类 * @author limenghua * */ public class BaseEntity implements Serializable { private static final long serialVersionUID = 4861363281663993175L; /** * toString * 打印...
  • C++ 虚基类

    千次阅读 多人点赞 2021-05-13 12:07:25
    C++ 虚基类. 虚基类如何解决多重继承的问题.
  • C++ 定义基类和派生类

    2021-05-22 20:18:37
    定义基类和派生类 定义基类 class Quote { public: Quote() = default; Quote(const string &book, double sales_price): bookNo(book), price(sales_price) {} string isbn() const { return bookNo; } /...
  • Date类中有很方法在JDK1.0公布后已经过时了,在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。 在日期类中共定义了六种构造函数。 (1)public Date() 创建的日期类对象的日期时间被设置成创建时刻相...
  • Consider the following code:class Base(object):@classmethoddef do(cls, a):print cls, aclass Derived(Base):@classmethoddef do(cls, a):print 'In derived!'# Base.do(cls, a) -- can't pass `cls`Base.do(a)i...
  • 一、基类 与 派生类 1、“属于”(is-a)和 “具有”(has-a)的区别 2、创建派生类的格式 3、举例:圆是点的子类 二、异常基类 1、exceptionBaseException 2、args 3、with_traceback(tb) 4、exception...
  • 组件化之路 - ViewBinding基类封装

    千次阅读 2021-11-04 18:56:54
    使用View Binding 写的基类 通过反射的方式(这种方式不推荐使用,会有混淆问题) //Java public class BaseActivity<T extends ViewBinding> extends AppCompatActivity { protected T viewBinding; @...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 519,162
精华内容 207,664
关键字:

多基类