C/C++开发语言系列之5---普通继承和虚基类构造函数的初始化顺序2

虚基类 : 
如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些基类中,从上一级基类继承来的成员就有相同的名称,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性,此时,可定义虚基类。这就要求在其直接基类的定义中,使用关键字 virtual 将那个共同的基类定义为虚基类,其语法形式如下: 

       class  派生类名:   virtual 派生方式 基类 


    虚基类的初始化与一般的多重继承的初始化在语法上是一样的 ,但构造函数的调用顺序不同,虚基类构造函数的调用顺序是这样规定的: 
1) 在同一层次中先调用虚基类的构造函数,接下来依次是非虚基类的构造函数,对象成员的构造函数,派生类的构造函数。 
2) 若同一层次中包含多个虚基类,这些虚基类的构造函数按对他们说明的先后次序调用 
3) 若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类构造函数。


///1 普通情况
#include<iostream>  
using namespace std;

/*

A1

|

|

B1


 输出结果

constructing A1
constructing B1
~~Deconstructing B1
~~Deconstructing A1
*/

class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  
  
class B1:public A1{  
public:  
    B1(int sa,int sb):A1(sa)  
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


int main()  
{  
B1 b1(1,2);


    return 0;  
}  



///2 多重继承
#include<iostream>  
using namespace std;

/*

A1  A2

|        |

|        |

   B1


 输出结果

constructing A2   与继承的顺序有关
constructing A1
constructing B1
~~Deconstructing B1
~~Deconstructing A1
~~Deconstructing A2
*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  


//第2个基类
class A2{  
public:  
    A2(int sa)  
    {  
        a2=sa;  
        cout<<"constructing A2"<<endl;  
    }
    ~A2()  
    {   
        cout<<"~~Deconstructing A2"<<endl;  
    }
protected:  
    int a2;  
};  




class B1:public A2,public A1{  
public:  
    B1(int sa,int sb):A1(sa),A2(sa) //基类的调用顺序与这里的顺序无关
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};

int main()  
{  
    B1 b1(1,2);
    return 0;  
}  




/3.多重继承2
#include<iostream>  
using namespace std;


/*

A1  A2

|        |

|        |

   B1

    |

    |

   C1

 输出结果

constructing A2
constructing A1
constructing B1   先调用父类的父类A2,和A1,虽然我们没有在C1中直接调用A2,和A1
constructing C1

~~Deconstructing C1
~~Deconstructing B1
~~Deconstructing A1
~~Deconstructing A2
*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  


//第2个基类
class A2{  
public:  
    A2(int sa)  
    {  
        a2=sa;  
        cout<<"constructing A2"<<endl;  
    }
    ~A2()  
    {   
        cout<<"~~Deconstructing A2"<<endl;  
    }
protected:  
    int a2;  
};  

class B1:public A2,public A1{  
public:  
    B1(int sa,int sb):A1(sa),A2(sa)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


class C1:public B1{  
public:  
    C1(int sa,int sb,int sc):B1(sa,sb)
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};


int main()  
{  
C1 c1(1,2,3);
    return 0;  
}  




/4. 带有一个虚基类的继承初始化

#include<iostream>  
using namespace std;


/* 

A1  A2

|        |

|        |

   B1

输出结果

constructing A1 //virtual 继承优先于普通继承,所以即使继承顺序为A2,A1,也优先调用A1
constructing A2
constructing B1

~~Deconstructing B1
~~Deconstructing A2
~~Deconstructing A1
*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  


//第2个基类
class A2{  
public:  
    A2(int sa)  
    {  
        a2=sa;  
        cout<<"constructing A2"<<endl;  
    }
    ~A2()  
    {   
        cout<<"~~Deconstructing A2"<<endl;  
    }
protected:  
    int a2;  
};  


//这里指定虚拟继承A1,即使A2在A1前面,也会先调用virtual继承的父类A1
class B1:public  A2,public virtual A1{  
public:  
    B1(int sa,int sb):A1(sa),A2(sa)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:
    int b1;   
};


int main()  
{  
B1 b(1,2);
    return 0;  
}  


/5. 带有一个虚基类的继承2, B1又带有一个子类
#include<iostream>  
using namespace std;

/* 输出结果

A1  A2

|        |

|        |

   B1

    |

    |

   C1



constructing A1  //virtual 继承优先于普通继承,所以即使继承顺序为A2,A1
constructing A2
constructing B1
constructing C1


~~Deconstructing C1
~~Deconstructing B1
~~Deconstructing A2
~~Deconstructing A1
*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
A1()  
    {  
        a1=1;  
        cout<<"constructing A1**"<<endl;  
    }

    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  


//第2个基类
class A2{  
public:  
    A2(int sa)  
    {  
        a2=sa;  
        cout<<"constructing A2"<<endl;  
    }
    ~A2()  
    {   
        cout<<"~~Deconstructing A2"<<endl;  
    }
protected:  
    int a2;  
};  




class B1:public  A2,public virtual A1{  
public:  
    B1(int sa,int sb):A1(sa),A2(sa)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


//B1还有一个子类
class C1:public  B1{  
public: 
//基类B1有虚基类A1,所以需要手动指定带有参数的初始化函数,
//否则系统自动找无参数的构造函数(如A1只定义了有参数的构造函数,未定义无参的构造函数,编译器将报错)
//因为如果自己定义了个有参数的构造函数,则系统不会为我们创建无参的构造函数

    C1(int sa,int sb,int sc):B1(sa,sb) ,A1(sa)//这里可以去除A1(sa),因为A1中已经定义了一个无参的构造函数
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};




int main()  
{  
C1 b(1,2,3);
    return 0;  
}


/6. 第6种情况virtual所有的子类都要显式的调用虚基类构造函数A1


#include<iostream>  
using namespace std;

/* 

A1 

|     

|     

B1

 |

 |

C1


输出结果

 编译器报错,注释中有解释和说明

*/
//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  

class B1:public virtual A1{  
public:  
    B1(int sa,int sb):A1(sa)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};

class C1:public  B1{  
public: 
//基类B1有虚基类A1,所以C1中构造函数需要手动指定带有参数的初始化函数A1(sa) 爷爷类,
//否则系统自动找无参数的构造函数(如A1只定义了有参数的构造函数,未定义无参的构造函数,编译器将报错)
    //看这里例子,因为B1虚拟继承了A1,即使B1中显式的调用了A1的构造函数A1(sa),但还是报错
//编译器报错:error C2512: 'A1::A1' : no appropriate default constructor available
//修改方法是1.去掉B1 继承A1时候的virtual 
//2. C1中显式的调用A1构造函数,构造函数C1(int sa,int sb,int sc):B1(sa,sb),A1(sa)
//3. A1中加入一个无参数的构造函数, 视情况而定
//所以,C1和C1 的子类也是需要显式的调用祖宗类A1(sa)


C1(int sa,int sb,int sc):B1(sa,sb)//编译器报错
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};




int main()  
{  
C1 b(1,2,3);
    return 0;  
}


/7. 第7种情况:通过作用域符号:: 解决二义性
C1有两个父类B1,B2,但是B1和B2还有共同的父类A1
#include<iostream>  
using namespace std;




/* 

         A1

   |            | 

   |           | 

   B1      B2

         |

         |

        C1


输出结果

C1中show函数,使用作用域符号::, cout<<B1::a1<<endl;  B1::a1 的结果
constructing A1
constructing B2
constructing A1 //A1被调用了2次,不是我们想要的结果,继承了两份
constructing B1
constructing C1
2  这里调用B1的继承的变量,与B2中继承的是不同的copy,内容不同   B1::a1=2  B2::a1=0
~~Deconstructing C1
~~Deconstructing B1
~~Deconstructing A1
~~Deconstructing B2
~~Deconstructing A1
Press any key to continue
*/
//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  




class B1:public  A1{  
public:  
    B1(int sa,int sb):A1(sa+1)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


class B2:public  A1{  
public:  
    B2(int sa,int sb):A1(sa-1)
    {  
        b2=sb;  
        cout<<"constructing B2"<<endl;  
    }
~B2()
    {  
        cout<<"~~Deconstructing B2"<<endl;  
    } 
protected:  
    int b2;   
};


class C1:public  B2,public B1{  
public: 
C1(int sa,int sb,int sc):B1(sa,sb),B2(sa,sb)
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
void show()
{
/*
这行代码有二义性,这里不知道是B1::a1 还是B2::a1
解决方法:1. B1和B2虚拟继承A1 virtual  2.使用作用域符号:: B1::a1 或B2::a1
当然不能出现这样的表达式:A1::B1::a1
*/
cout<<a1<<endl;  
}


~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};




int main()  
{  
C1 c(1,2,3);
c.show();
    return 0;  
}


/8. 第8 种情况:通过虚基类 解决二义性
参考:
http://stackoverflow.com/questions/3495139/calling-a-virtual-base-classs-overloaded-constructor


#include<iostream>  
using namespace std;




/* 
主要更改:
1. B1 B2 继承的时候添加virtual
2. B1 B2的子类C1构造函数中调用A1的构造函数,A1(sa),或在A1中添加一个默认的构造函数
3. C1中直接通过名字 a1 进行访问
4. B1 B2构造函数对A1的初始化就没有运行了,但是还不能去掉B1 B2构造函数对A1的初始化---编译期间的语法规定,运行时候动态决定

  

        A1

   |            | 

  虚        虚

   |           | 

   B1      B2

         |

         |

        C1



输出结果
constructing A1   只调用了一次。在C1构造函数中指定A1(sa)
constructing B2
constructing B1
constructing C1
1
~~Deconstructing C1
~~Deconstructing B1
~~Deconstructing B2
~~Deconstructing A1
Press any key to continue


*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }


    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  




class B1:public virtual A1{  
public:  
    B1(int sa,int sb):A1(sa+1)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


class B2:virtual public  A1{  
public:  
    B2(int sa,int sb):A1(sa-1)
    {  
        b2=sb;  
        cout<<"constructing B2"<<endl;  
    }
~B2()
    {  
        cout<<"~~Deconstructing B2"<<endl;  
    } 
protected:  
    int b2;   
};


class C1:public  B2,public B1{  
public:
//这里需要A1(sa),注意这里得到值的a1 为A1::a1 =1, 且B1::a1=1 B2::a1=1
C1(int sa,int sb,int sc):B1(sa,sb),B2(sa,sb),A1(sa)
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
void show()
{
/*
*/
cout<<B2::a1<<endl;  
}


~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};




int main()  
{  
C1 c(1,2,3);
c.show();
    return 0;  
}


具体可以看网友的总结(需要细细品味):
虚基类声明,初始化及调用顺序
http://blog.csdn.net/opk625153475/article/details/7798035
1.如果虚基类中定义有带参数的构造函数,并且没有默认定义构造函数,则整个继承结构中,所有直接或间接的派生类必须在构造函数的成员初始化列表中列出虚基类构造函数的调用。
2.建立对象时,如果这个对象中含有从基类继承来的成员,则虚基类的成员有最远派生类的构造函数通过调用虚基类的构造函数进行初始化的,该派生类的其他基类对虚基类构造函数的调用都自动忽略。
3.如果同一层次中同时有虚基类和非虚基类,则先调用虚基类构造函数,在调用非虚基类构造函数,最后调用派生类构造函数。
4.当有多个虚基类,或多个非虚基类时,调用:先左后右,自上而下。
5.如虚基类由非虚基类派生来,则先调用基类构造函数,在调用派生类构造函数。
class 派生类:virtual  继承方式 类名{- - - - -}

例子如下:


#include<iostream>  
using namespace std;  
class Base{  
public:  
    Base(int sa)  
    {  
        a=sa;  
        cout<<"constructing Base"<<endl;  
    }  
protected:  
    int a;  
};  
  
class Base1:virtual public Base{  
public:  
    Base1(int sa,int sb):Base(sa)  
    {  
        b=sb;  
        cout<<"constructing Base1"<<endl;  
    }  
protected:  
    int b;   
};  
class Base2:virtual public Base{  
public:  
    Base2(int sa,int sc):Base(sa)  
    {  
        c=sc;  
        cout<<"constructing Base2"<<endl;  
    }  
protected:  
    int c;  
};  
class Deriverd:public Base1,public Base2{  
public:  
    Deriverd(int sa,int sb,int sc,int sd):Base(sa),Base1(sa,sb),Base2(sb,sc)  
    {      
        d=sd;  
        cout<<"constructing Deriverd "<<endl;  
    }  
    void show()  
    {  
        cout<<"a="<<a<<endl;  
        cout<<"b="<<b<<endl;  
        cout<<"c="<<c<<endl;  
        cout<<"d="<<d<<endl;  
    }  
private:  
    int d;  
  
};  
int main()  
{  
    Deriverd dd(10,20,30,40);  
    dd.show();  
    return 0;  



///9. 复杂情况

class A1;
class A2;
class B1 : public A2, virtual public A1;
class B2 : public A2, virtual public A1;
class C1 : public B1, virtual public B2;
C1 c;


构造函数调用顺序如下:
A1(); // 虚基类仅被构造一次
A2();
B2(); // 虚基类
A2();
B1();
C1();


原则:

如果类继承中包括多个虚基类的实例,基类只被初始化一次。

1、如果类里面有成员类,成员类的构造函数优先被调用;

2、创建派生类的对象,基类的构造函数函数优先被调用(也优先于派生类里的成员类);

3、 基类构造函数如果有多个基类则构造函数的调用顺序是某类在类派生表中出现的
顺序而不是它们在成员初始化表中的顺序;


4、成员类对象构造函数如果有多个成员类对象则构造函数的调用顺序是对象在类中
被声明的顺序而不是它们出现在成员初始化表中的顺序;


5、派生类构造函数
作为一般规则派生类构造函数应该不能直接向一个基类数据成员赋值而是把值传递
给适当的基类构造函数否则两个类的实现变成紧耦合的(tightly coupled)将更加难于
正确地修改或扩展基类的实现。(基类设计者的责任是提供一组适当的基类构造函数)




实现代码:
#include<iostream>  
using namespace std;

/* 
从下到上查找,virtual优先(只运行一次),父类优先
输出结果
constructing A1
constructing A2
constructing B2
constructing A2
constructing B1
constructing C1


~~Deconstructing C1
~~Deconstructing B1
~~Deconstructing A2
~~Deconstructing B2
~~Deconstructing A2
~~Deconstructing A1
*/

//第1个基类
class A1{  
public:  
    A1(int sa)  
    {  
        a1=sa;  
        cout<<"constructing A1"<<endl;  
    }
    ~A1()  
    {   
        cout<<"~~Deconstructing A1"<<endl;  
    }
protected:  
    int a1;  
};  

//第2个基类
class A2{  
public:  
    A2(int sa)  
    {  
        a2=sa;  
        cout<<"constructing A2"<<endl;  
    }
    ~A2()  
    {   
        cout<<"~~Deconstructing A2"<<endl;  
    }
protected:  
    int a2;  
};  

class B1:public  A2,public virtual A1{  
public:  
    B1(int sa,int sb):A1(sa),A2(sa)
    {  
        b1=sb;  
        cout<<"constructing B1"<<endl;  
    }
~B1()
    {  
        cout<<"~~Deconstructing B1"<<endl;  
    } 
protected:  
    int b1;   
};


class B2:public  A2,public virtual A1{  
public:  
    B2(int sa,int sb):A1(sa),A2(sa)
    {  
        b2=sb;  
        cout<<"constructing B2"<<endl;  
    }
~B2()
    {  
        cout<<"~~Deconstructing B2"<<endl;  
    } 
protected:  
    int b2;   
};

//B1还有一个子类
class C1:public  B1,public virtual B2{  
public: 
    C1(int sa,int sb,int sc):B1(sa,sb),B2(sa,sb),A1(sa)
    {  
        c1=sc;  
        cout<<"constructing C1"<<endl;  
    }
~C1()
    {  
        cout<<"~~Deconstructing C1"<<endl;  
    } 
protected:  
    int c1;   
};

int main()  
{  
C1 c(1,2,3);
    return 0;  
}




以上10套代码全部亲测通过

结果已经贴在每段代码中

欢迎学习交流




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值