C++高级编程【继承】

文章目录

  • 类内成员的权限
  • 继承的作用
  • 继承方式
  • 继承中的构造和析构顺序
  • 初始化列表在继承中的作用
  • 继承中同名成员处理
  • 继承和组合情况下构造和析构调用顺序
  • 不能继承的函数
  • 多继承
  • 菱形继承的问题
  • 虚继承
  • 虚基类初始化问题

类内成员的权限

public private protected
类内、类外、子类都可以访问 类内可以访问,类外不能访问,子类不能访问 类内可以访问,类外不能访问,子类可以访问

类内部的成员权限决定了子类能否被继承
只有public 和protected 权限的成员才能被子类继承

C++ 最重要的特征是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员

继承的作用

  • 复用已有类的数据、函数,减少冗余代码。

    将公共的函数写在基类中,通过继承来实现复用

  • 修改已有类的功能,而不修改原类。

    如果类内部的函数不能满足需求时,需要修改该函数的功能。
    修改源代码的代价很大,所以应该使用重定义该函数来实现我们自己的需求
    当子类重定义父类的函数时,父类的函数会被隐藏

  • 扩展已有类的功能,而不修改原类。
  • 为实现强大的多态机制做支撑。

继承方式

  • 继承的方式决定了被继承下来的成员在子类中的权限
public private protected
基类什么权限,继承下来就是什么权限 继承下来的成员在子类中全部为private权限 继承下来的成员在子类中全部为protected权限

继承中的构造和析构顺序

class Base
{
public:
	Base()
	{
		cout<<"base构造函数"<<endl;
	}


	~Base()
	{
		cout<<"base析构函数"<<endl;
	} 
}

class Derived : public Base
{
public:
	Derived()
	{
		cout<<"Derived构造函数"<<endl;
	}
	~Base()
	{
		cout<<"Derived析构函数"<<endl;
	} 
}

void test()
{
		Derived der;
}

创建对象时,首先调用父类的构造,在调用子类的构造
销毁对象时,首先调用子类的析构,在调用父类的析构

初始化列表在继承中的作用

子类初始化时,编译器默认调用父类的无参构造,当父类没有提供无参构造时,
需要使用初始化列表指定调用父类的哪一个构造函数

class Base
{
public:
	Base(int a)
	{
		cout<<"base构造函数"<<endl;
	}


	~Base(){}
}

class Derived : public Base
{
public:
	Derived(int num) :Base(num)
	{
		cout<<"Derived构造函数"<<endl;
	}
	~Base(){} 
}


void test()
{
	Derived der(10);
}	

继承中同名成员处理

  • 当子类成员和父类成员同名,父类同名成员会被隐藏。
  • 子类默认访问子类的成员。
  • 子类内部通过 类名::成员 的方式访问父类同名成员。
  • 任何时候重新定义基类中的一个重载函数,在新类中所有的其他版本将被自动隐藏。

继承和组合情况下构造和析构调用顺序

当其他类作为当前类的对象成员,这种关系为组合关系
初始化顺序

  1. 先调用父类构造函数。
  2. 在调用对象成员构造函数,按照定义顺序。
  3. 最后调用子类构造函数。
  4. 析构函数调用与构造函数调用相反。

不能继承的函数

  1. 私有权限的函数。
  2. 构造函数。
  3. 析构函数。
  4. 赋值运算符函数。

构造函数和析构函数用来处理对象的创建和析构操作,构造和析构函数只知道对它们的特定层次的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每一个特定的派生类分别创建。

operator=也不能被继承,因为它完成类似构造函数的行为。也就是说尽管我们知道如何由=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派生类依然有效

多继承

  • 多继承可以复用多个类的代码
  • 多继承容易出现二义性的问题
  • 二义性只能通过类名访问来解决(应该避免二义性)

菱形继承的问题

二义性问题。
子类中存在多份基类数据。
使用虚继承可以解决菱形继承的问题

虚继承

当子类使用虚继承后,父类就变成了虚基类
如果一个类时虚基类,该类的数据在所有的子类中只会出现一份
虚继承主要是解决菱形继承的问题

语法

class Animal
{
public:
    Animal()
    {
        cout << "Animal 构造函数" << endl;
    }

    ~Animal({
    }
public:
    int m_animal;
};
//虚继承
class Demo : virtual public Animal
{
public:
	int m_demo;
}

虚基类初始化问题

当使用虚继承时,虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象。

C++ 标准中要求在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),但是虚基类的初始化是由最后的子类完成,其他的初始化语句都不会调用。

虚继承总结

当发生虚继承时,子类没有将基类的数据继承到本类中,编译器会在子类中安插vbptr指针
vbptr 指针指向保存着基类数据偏移量的表,通过该表找到基类数据。

你可能感兴趣的:(C++,c++,继承)