【C/C 】继承

概述

  • 继承与派生是同一过程从不同的角度看

    • 保持已有类的特性而构造新类的过程称为继承
    • 在已有类的基础上新增自己的特性而产生新类的过程称为派生
  • 被继承的已有类称为基类(父类)

  • 派生出的新类称为派生类(子类)

  • 直接参与派生出某类的基类称为直接基类

  • 基类的基类甚至更高层的基类称为间接基类

  • 继承的目的是为了实现设计与代码的重用

  • 派生则是因为当新的问题出现,原有程序无法解决时,需要对原有程序进行改造

  • 默认情况下派生类包含了全部基类中除构造和析构函数之外的所有成员(C 11规定可以用using语句继承基类构造函数)

  • 如果派生类声明了一个和某基类成员同名的新成员,派生的新成员就隐藏或覆盖了外层的同名成员

语法

单继承

/*
class 派生类名 : 继承方式  基类名
{
    成员声明 ;
}
*/
class Derived :public Base
{
public:
    Derived() ;
    ~Derived() ;
}

多继承

/*
class 派生类名 : 继承方式1  基类名1, 继承方式2  基类名2
{
    成员声明 ;
}
每一个继承方式,只用于限制对紧随其后的基类的继承
*/
class Derived :public Base1, private Base2
{
public:
    Derived() ;
    ~Derived() ;
}

继承方式

不同继承方式的影响主要体现在:

  • 派生类成员对基类成员的访问权限
  • 通过派生类对象对基类成员的访问权限

三种继承方式:

  • 公有继承(public)

    • 派生类中的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
    • 通过派生类的对象,只能访问public成员
  • 私有继承(private)
    • 派生类中的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
    • 通过派生类的对象,不能直接访问从基类继承的任何成员
  • 保护继承(protected)
    • 派生类中的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
    • 通过派生类的对象,不能直接访问从基类继承的任何成员

类型转换

  • 公有派生类对象可以被当作基类的对象使用,反之则不行

    • 派生类的对象可以隐含转换为基类对象
    • 派生类的对象可以初始化基类的引用
    • 派生类的指针可以隐含转换为基类的指针
  • 通过基类对象名,指针只能使用从基类继承的成员

派生类的构造函数

默认情况下

  • 基类的构造函数不被继承
  • 派生类需要定义自己的构造函数

(C 11中,可以使用using语句继承基类构造函数,但只能初始化从基类继承的成员,语法形式using B::B

  • 派生类新增成员:派生类定义构造函数初始化
  • 继承的成员:自动调用基类构造函数进行初始化
  • 派生类的构造函数需要给基类的构造函数传递参数

单继承

#include <iostream>
using namespace std;
class B {
public:
    B();
    B(int i);
    ~B();
    void print() const;
private:
    int b;
};
B::B() {
    b = 0;
    cout << "B's default constructor called" << endl;
}
B::~B() {
    cout << "B's destructor called" << endl;
}
B::B(int i) {
    b = i;
    cout << "B's constructor called" << endl;
}
void B::print() const {
    cout << b << endl;
}

class C :public B {
public:
    C();
    C(int i, int j);
    ~C();
    void print() const;
private:
    int c;
};
C::C() {
    c = 0;
    cout << "C's default constructor called" << endl;
}

C::C(int i, int j) : B(i), c(j) {
    cout << "C's constructor called" << endl;
}
C::~C() {
    cout << "C's destructor called" << endl;
}
void C::print() const {
    B::print();
    cout << c << endl;
}
int main() {
    C c(2, 3);
    c.print();
    return 0;
}
/*
B's constructor called
C's constructor called
2
3
C's destructor called
B's destructor called
*/

多继承

#include <iostream>
using namespace std;
class Base1 {
public:
    Base1(int i) {
        cout << "Constructing Base1 " << i << endl;
    }
};
class Base2 {
public:
    Base2(int j) {
        cout << "Constructing Base2 " << j << endl;
    }
};
class Base3 {
public:
    Base3() {
        cout << "Constructing Base3" << endl;
    }
};
/*
    先按照继承的顺序,对继承过来的成员调用基类的构造函数进行初始化
    然后给派生类的新增成员进行初始化 member1, member2, member3
*/
class Derived : public Base2, public Base1, public Base3 {
public:
    Derived(int a, int b, int c, int d) : Base1(a), Base2(b), member2(c), member1(d) {}
private:
    Base1 member1;
    Base2 member2;
    Base3 member3;
};
int main() {
    Derived obj(1, 2, 3, 4);
    return 0;
}
/*
Constructing Base2 2
Constructing Base1 1
Constructing Base3
Constructing Base1 4
Constructing Base2 3
Constructing Base3
*/

派生类的复制构造函数

若派生类没有声明复制构造函数:

  • 编译器会生成一个隐含的复制构造函数
  • 先调用基类的复制构造函数
  • 再为派生类新增的成员进行复制

若派生类定义复制构造函数

  • 一般都要为基类的复制构造函数传递参数

  • 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将被传递给基类的复制构造函数

  • 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用

    C::C(const C & c1): B(c1) ...{ ... }
    

派生类的析构函数

  • 析构函数不会被继承
  • 不需要显式调用基类的析构函数,系统会自动隐式调用
  • 先执行派生类析构函数,再调用基类的构造函数

访问从基类继承的成员

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

  • 若没有特别限定,则通过派生类对象使用的是派生类中的同名成员
  • 如果要通过派生类对象访问基类中被隐藏的同名成员,则应该使用基类名和作用域操作符(::)来限定

二义性问题

如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员,就产生二义性问题

解决方式:用类名限定

虚基类

当派生类从多个基类派生,而这些基类又有一个共同的基类,则在访问此共同基类中的成员时,将产生冗余,虚基类就是用于解决这一问题

class B1 : virtual public B ......

虚基类的作用:

  • 主要用来解决多继承时,可能发生的对同一基类继承多次而产生的二义性问题
  • 为最远的派生类提供唯一的基类成员,而不重复产生多次复制
  • 注意,在第一级继承时,就要将共同基类设计为虚基类

虚基类的构造函数:

  • 建立对象时所指定的类称为最远派生类
  • 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中为虚基类的构造函数列出参数。如果未列出,则调用该虚基类的默认构造函数
  • 在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,其他类对虚基类构造函数的调用被忽略
#include <iostream>
using namespace std;
class Base0 {
public:
    int var0;
    Base0(int var) : var0(var) {}
    void fun0() {
        cout << "Member of Base0" << endl;
    }
};
class Base1 : virtual public Base0 {
public:
    Base1(int var) : Base0(var), var1(var) {}
    int var1;
};
class Base2 : virtual public Base0 {
public:
    Base2(int var) : Base0(var), var2(var) {}
    int var2;
};
class Derived : public Base2, public Base1{
public:
    Derived(int var) : Base0(var), Base1(var), Base2(var) {}
};
int main() {
    Derived obj(1);
    cout << obj.var0 << endl;
    return 0;
}
/*
1
*/

来源:https://www.icode9.com/content-1-880551.html

(0)

相关推荐