派生类继承构造函数
如果基类含多个构造函数,多数情况下,派生类一般会集成所有构造函数,以下几种情况比较特殊
- 1、如果你在派生类中定义的构造函数与基类构造函数有
相同的参数列表
,那么从基类中继承的会被覆盖掉
也就是只继承了及部分构造函数
- 2、拷贝,移动构造函数不会被继承
- 3、using classA::classA,这不属于自己定义构造函数,编译器还是会合成一个默认的无参构造函数,但是这样声明了之后,父类必须有无参构造函数
自己管自己的父类
一个类只继承直接基类的(父类)的构造函数
。默认,拷贝,移动构造函数不能被集成
class classA
{
public:
//classA(int i, int j, int k){}
classA(int i, int j, int k = 0){}
classA() {}
};
class classB:public classA
{
public:
//classB(int i, int j, int k) :classA(i, j, k){}
using classA::classA;
};
这样看来,using就是让某个函数名字可见
遇到这条代码是,编译器会把基类中每个构造函数
,都生成一个与之对应的构造函数
但是到底长什么样呢?
classB(参数列表):classA(照抄父类参数列表){}
,比如这个
classB(int i,int j,int k):classA(i,j,k){}
如果有默认参数的话
,那么编译器遇到using classA::classA的时候
就会帮我们在派生类中构造出多个构造函数出来
- 1、带所有参数的构造函数
- 2、其余分别少一个默认参数
例如classA(int i, int j, int k = 0)
如果使用using classA::classA,那么就会有如下的默认构造函数生成
1、classB(int i , int j , int k):A(i,j,k){}
2、classB(int i , int j):A(i,j){}
多重集成
class grandFather
{
public:
int m_gf;
public:
//这里参数列表赋值
grandFather(int i) :m_gf(i)
{
cout << "grandFather构造函数" << endl;
}
void myInfo()
{
cout << m_gf << endl;
}
virtual ~grandFather()
{
cout << "grandFather析构函数" << endl;
}
};
class father :public grandFather
{
public:
int m_f;
public:
//每个子类的构造函数,负责解决自己父类的初始化问题
//每个类都要解决直接父类的初始化问题
//不然只会调用默认的无参构造函数
//想一想,java的双亲委托机制是不是就是这样实现的?
father(int i) :m_f(i), grandFather(m_f)
{
cout << "father构造函数" << endl;
}
virtual ~father()
{
cout << "father析构函数" << endl;
}
void myInfo()
{
cout << m_f << endl;
}
};
class others
{
public:
int m_o;
public:
//每个子类的构造函数,负责解决自己父类的初始化问题
others(int i) :m_o(i)
{
cout << "others构造函数" << endl;
}
virtual ~others()
{
cout << "others析构函数" << endl;
}
void myInfo()
{
cout << m_o << endl;
}
};
//派生列表
class classSon :public father, public others
{
public:
int m_s;
public:
//有两个直接父类,都要把他们初始化
classSon(int i, int j, int k) :father(i), others(j),m_s(k)
{
cout << "classSon构造函数" << endl;
}
virtual ~classSon()
{
cout << "classSon析构函数" << endl;
}
//因为父类函数冲突了,子类重写覆盖
void sonInfo()
{
cout << m_s << endl;
}
//遇到冲突的问题,可以直接在子类中,进行
//父类::函数/参数
void sonCallParentInfo()
{
father::myInfo();
others::myInfo();
sonInfo();
}
};
- 再多重继承中,派生类会包含每个基类的子对象
int main()
{
classSon class1(1, 2, 3);
class1.sonInfo();
}
执行结果:
- father覆盖了grandFather中的myInfo();如果子类继承的多个类中都有myInfo这个函数,就要显示声明调用的是哪个函数
classSon class1(1, 2, 3);
class1.father::myInfo();
class1.others::myInfo();
静态成员变量
属于类,不属于对象
但是对象也是可以调用的
在grandFather中加上
class grandFather
{
public:
static int gf_SI;
}
//为了能够使用,要定义这个静态类
//没有用到就不用定义,使用必须定义,光声明是不行的
int grandFather::gf_SI = 666;
int main()
{
cout << grandFather::gf_SI << endl;
cout << father::gf_SI << endl;
cout << classSon::gf_SI << endl;
//对象也是可以的
cout << class1.gf_SI << endl;
}
派生类构造函数与析构函数
- 1、构造一个派生类对象,将同时构造初始化所有基类子对象
- 2、派生类的初始化列表只初始化它的直接基类,这样所有的都能初始化
- 3、派生类构造函数吃石化列表将实参分别传递给每个直接基类,基类的构造顺序由派生列表决定,派生列表:
class classSon :public father, public others
,跟初始化列表(classSon(int i, int j, int k) :father(i), others(j),m_s(k)
)没有关系
显式的初始化与隐士的初始化(使用默认构造函数,不带参数)
//新建了一个类others2,只有无参构造方法
class others2
{
public:
//是默认的无参构造方法
others2()
{
cout << "others2构造函数" << endl;
}
virtual ~others2()
{
cout << "others2析构函数" << endl;
}
};
class classSon :public father, public others,public others2
{
public:
int m_s;
public:
//虽然继承了others2类,但是由于只有无参构造函数,不显式初始化也是可以的
classSon(int i, int j, int k) :father(i), others(j),m_s(k)
{}
}
继承的两个构造函数参数重复,会导致程序错误,必须定义自己的版本
class A
{
public:
A(int val) {};
};
class B
{
public:
B(int val) {};
};
class C :public A, public B
{
public:
//这样继承了A,B的构造函数,但是一摸一样(只要参数列表一样就是一摸一样)
using A::A;
using B::B;
//必须定义自己的参数列表一样的去覆盖
C(int val) :A(val), B(val) {}
};
解释为什么基类指针可以指向子类对象
基类指针了一指向派生类对象,编译器会帮助我们进行转换,因为每个派生类对象
都包含
一个基类对象
区域,所以基类的指针和引用可以绑定到基类上
grandFather* g = new classSon(1, 2, 3);
虚基类,虚继承
派生列表中同一类只能出现一次,但是以下情况特别
- 1、通过两个直接基类,继承了两次间接基类
- 2、直接继承该基类,通过直接继承另一个类,又间接继承了该类
初始化多次,名字冲突,不能直接调用那个类的方法,因为不知道用哪一个的
如下:
我们重写others类,让它也和father一样继承grandfather类
class others:public grandFather
{
public:
int m_o;
public:
//初始化grandFather
others(int i) :m_o(i) ,grandFather(i)
{
cout << "others构造函数" << endl;
}
virtual ~others()
{
cout << "others析构函数" << endl;
}
void myInfo()
{
cout << m_o << endl;
}
};
int main()
{
classSon class1(1,2,3);
}
然后classSon类实际上通过father,others类继承了两次grandfather类,看一看初始化classSon类的效果
真的构造了两次,不行不行
解决方法:虚继承
首先,虚基类:无论这个类在继承体系中出现多少次,派生类中都只会包含唯一一个虚基类子内容
这种虚继承不对直接子类有意义,只对直接子类派生出来的类才有意义
最后所有虚基类的子类都要虚继承,才能保证孙类是虚继承
格式:class classA:virtual father,并且要用孙类的构造函数进行初始化
class classSon : public father, public others
{
public:
//如果父类是虚继承,那么初始化虚基类的任务就要交给最小辈儿的类来
classSon(int i, int j, int k) :grandFather(k),father(i), others(j),m_s(k)
{cout << "classSon构造函数" << endl;}
}
// 声明虚继承
class others:virtual public grandFather
{
public:
// 这里初始化也没什么用了
others(int i) :m_o(i) ,grandFather(i)
}
// 声明虚继承
class father :virtual public grandFather
{
public:
// 这里初始化也没什么用了
father(int i) :m_f(i), grandFather(m_f)
}
int main()
{
classSon class1(1,2,3);
}
执行效果:
说明两点:
- 1、现在是孙子类构造函数初始化,如果以后它再有子类,那么也要进行加上换句话说,就是最底层的进行初始化虚基类
- 2、虚基类不管派生列表的顺序,它就是首先进行初始化
- 3、多个虚基类,从派生列表的继承顺序,往回追溯,先找到那个虚基类,就先初始化哪个
总结:
- 小心虚继承,不太提倡使用
- 简单,不易出现二义性,实在必要才用。