C++基础(三)继承

1. 什么是继承

子类(派生类)是父类(基类)的子集,比父类有更多的属性。写法:class Worker : public Person{};。子类不用再定义父类中的成员,在实例化对象时会包含父类的数据成员和成员函数,子类实例化时先执行父类的构造函数,再执行子类的构造函数,析构时先执行子类的析构函数,再执行父类的构造函数。

2. 继承方式

三个访问限定符,public,protected和private,protected在不涉及继承的时候,和private特性是一样的,protected和private修饰的成员变量和成员函数都不能被对象直接访问。
公有继承,父类public下的成员被继承到子类的public下,父类protected下的成员被继承到子类的protected下,父类private下的成员被继承到子类的不可见位置,成员函数无法访问。
在这里插入图片描述
在这里插入图片描述
类似的,还有保护继承私有继承,三种继承方式如下表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Has a:这种关系是一种包含关系,比如类的对象只能直接访问公有数据成员和公有成员函数,在私有继承中也是一种包含关系,当定义一个子类对象时,它就包含了父类的对象,因为它只能访问父类的公有数据成员和成员函数。

3. 继承中的特殊关系

  1. 隐藏
    假设有父类A,子类B公有继承了A,A中有成员ABC,B中也有成员函数ABC,这时B中应该继承了A的ABC,那么B中的ABC就隐藏了继承来的ABC,体现在实例化B的对象时,该对象只能直接访问到B的ABC函数,但A中的ABC还是存在的,通过A::ABC()的形式访问。
    如果A和B都定义了同名的成员变量abc,B中的abc也会隐藏掉A中的abc(变量类型不同也会隐藏),B的成员函数只能直接访问到B的abc,如果想访问A的abc,就要通过A::abc的形式。举例如下:
class Person
{
public:
	void play();
protected:
	int abc;
};
class Soldier:public Person
{
public:
	void play();
	void print_abc()
	{
		cout<<abc<<endl;//访问Soldier中定义的abc
		cout<<Person::abc<<endl;//访问Person中定义的abc
	}
	int abc;
};
//main中
Soldier soldier;
soldier.play();//调用Soldier中定义的play
soldier.Person::play();//调用Person中定义的play
  1. isA
    还是用Person和Soldier的例子说明:
Soldier s1;
Person p1 = s1;//正确,派生类可以给基类赋值
Person *p2 = &s1;//正确,基类指针可以指向派生类(派生类可以取地址给基类)
s1=p1;//错误
Soldier *s2=&p1;//错误

在实例化子类Soldier时会先调用Person的构造函数,再调用Soldier的构造函数。析构时顺序相反。
Person p1 = s1会调用父类的拷贝构造函数,销毁时会先调用父类的析构函数。Person *p2 = &s1不会调用拷贝构造函数,也不会析构。
这个特性可以用在函数传参时:

void func1(Person *p)
{
    ......
}
void func2(Person &p)
{
    ......
}
void func3(Person p)
{
    ......
}
//main.cpp
int main()
{
    Person p1;
    Soldier s1;
    func1(&p1);     func2(p1);		func3(p1)
    func1(&s1);     func2(s1);		func3(s1)
    //正确,把基类的指针或者是基类的对象或者是基类的引用作为函数的参数来使它可以接收所传入的子类的对象,并且也可以传入基类的对象
    return 0;
}

要注意的是,无论是对象赋值还是指针赋值,子类中多出来的自己的成员都会被截断,赋给的父类对象访问不到。存储结构如下图:
在这里插入图片描述
在这里插入图片描述
基类的指针指向派生类的内存空间,销毁基类的对象时,执行的是基类的析构函数,派生类的内存并没有释放,为了防止内存的泄漏,此时需要用virtual关键字修饰析构函数。virtual可以被继承,给基类的析构函数加一个关键字virtual,使销毁基类的对象时,可以释放派生类的对象的内存。virtual ~Person();子类可以写也可以不写,建议写上。这样就会先执行子类的析构函数,再执行父类的析构函数。

4. 多重继承和多继承

  1. 多重继承
    B继承A,C继承B,就是多重继承
  2. 多继承
    C既继承了A,又继承了B,就是多继承
    class C:public A,public B{ };public如果不写,系统默认是private
  3. 虚继承
    多继承+多重继承会形成菱形继承,如下图:在这里插入图片描述
    这样D中就会有两份完全一样的A的数据,解决方法就是虚继承。
    class B:virtual public A{ }; virtual也可以放在public后面。
    class C:virtual public A{ };
    B,C为虚基类。
    这样,class D:public B,public C{ };实例化D时对象中就只有一份A的数据了。

整理自慕课网c++远征

猜你喜欢

转载自blog.csdn.net/weixin_43927408/article/details/88324140