继承
继承指由已有类为基类(父类)进行继承出一个派生类(子类),实现类与类之间的复用,可以减少代码的冗余性,实现代码的重用,并且通过少量的修改,满足不断变化的具体应用要求,提高程序设计的灵活性。由已有类为基类(父类)进行继承出一个派生类(子类)。派生类由基类成员和派生类成员构成,基类负责定义在层次关系中所有类共同拥有的成员,派生类定义各自拥有的成员。
继承可以分为单继承和多继承,单继承指派生类只有一个父类,多继承指派生类可以有多个父类。
权限
类的访问权限在继承中有三种体现:
- public:类外、派生类都可以访问。
- protected:类外不能访问,派生类可以访问。
- private:类外、派生类都无法访问。
三种继承方式指定的派生类权限:
- public:公有继承
public->public
protected->protected
private->private - protected:保护继承
public->protected
protected->protected
private->private - private:
public->private
protected->private
private->private
派生类与基类的联系
派生类不能继承基类的成员:
- 构造函数
- 析构函数
- 友员
- 运算符重载
基类构造
派生类在构造函数中先调用基类的构造函数(先进入派生类构造),析构函数的调用顺序与构造函数调用顺序相反。在派生类的构造函数定义中,要提供基类构造函数所需要的参数。
基类构造函数注意点:
- 当基类的构造函数没有参数,或没有显示定义的构造函数时,派生类可以不向基类传递构造函数,甚至派生类可以不定义构造函数。
- 当基类含有带参数的构造函数时,由于派生类不能继承基类的构造函数和析构函数,所以派生类必须定义构造函数以提供把参数传递给基类构造函数的方法。
class A
{
protected:
int a;
pubilc:
A(int x) : a(x) {}
};
class B : public A
{
private:
int b;
public:
B(int x, int y)
: A(x), b(y){} //基类初始化构造方法是列表构造,基类(参数列表)
}
int main()
{
B b(1, 2); //a=1,b=2
return 0;
}
对基类成员的调用
派生类对已经继承的基类成员进行调用是合法的,但是派生类成员不是基类所创造的对象,不能直接调用基类成员。这时候就需要对派生类进行处理来调用基类。处理方法是将派生类的指针或引用赋给基类。
设B类继承A类:
B b;
A *p = &b; //将派生类对象地址赋给A类指针
A &a = b; //将派生类引用赋给A
//p和a都可以完成对基类A成员的调用
基类成员函数的隐藏
隐藏指派生类的函数会屏蔽掉与其同名的基类函数(不考虑虚函数),隐藏说明:
- 只要基类与派生类函数同名,基类函数就会被隐藏
- 如果必须调用基类函数,则需要将派生类对象转换成基类的指针或引用进行调用。
- 设计时应尽量避免这种情况的发生
class A
{
public:
void output(int a){
cout << "A" << endl;
}
}
class B : public A
{
public:
void outoput(){
cout << "B" << endl;
}
}
int main()
{
B b;
b.output(); //输出为B
b.output(1); //语法报错。虽然两个函数参数不同,但是在不同的作用域并不会发生函数的重载,并且B的对象也无法访问A的成员。
A *p = &b;
A &a = b;
p->output(1); //输出A
a.output(1); //输出A
return 0;
}
多继承
一个类从多个基类派生称为多继承,不同的基类继承中间用逗号隔开。多继承的基类初识化顺序是按声明顺序从左往右。
多继承初识化如下:
class A {
protected:
int a;
public:
A(int a) {
cout << "A=" << a << endl;
}
};
class B {
protected:
int b;
public:
B(int b) {
cout << "B=" << b << endl;
}
};
class C
:public A, public B
{
protected:
int a;
public:
C(int a, int b, int c)
: B(b), A(a), a(c){} //虽然初始化列表基类B在前,但调用构造函数仍是调用被继承时先声明的基类A
private:
};
int main()
{
C(1, 2, 3);
return 0;
}
虚继承
在出现菱形继承(如下B与C都继承A,D同时继承B和C)时,那么基类A的成员在派生类D中就会出现多份,就会造成派生类D对基类A的成员访问不明确问题。
为解决二义性问题引入了虚继承。在继承前加上virtual
关键字使继承方式称为虚继承,如果有菱形继承那么在最终的派生类中只有一份基类成员,就不会产生访问不明确问题。
class A
{
public:
A(int a){
this->a = a;
}
private:
int a;
};
class B : virtual public A //继承前加virtual关键字使成为虚继承
{
public:
B(int a, int b)
:A(a)
{
this->b = b;
}
private:
int b;
};
class C : virtual public A
{
public:
C(int a, int c)
:A(a)
{
this->c = c;
}
private:
int c;
};
class D : public B, public C
{
public:
D(int a, int b, int c, int d) //派生类要为每个基类初始化提供条件
:A(a), B(a, b), C(a, c)
{
this->d = d;
}
private:
int d;
};