概念
假设基于A类,创建了B类,那么称A为B的父类,B为A的子类
子类会继承父类的成员变量及成员函数,但是不能继承构造、析构、运算符重载
假设又基于B创建了C,那么称B为C的直接基类,A为C的间接基类
继承按照字面意思就是继承,而不是包含,当然了在代码中,如果两个类只包含关系,也可以这么写,但是不建议。比如人继承了生物,人包含了生物的属性和方法,同样人包含了脑袋,但是不建议说人继承了脑袋。同样一粒米属于米类,即使一粒米长了翅膀,它也是米的一种,它是由米演变而来的,它继承了米的所有属性及方法,也演变了自己的属性和方法。
语法
class A
{
public:
int time;
}
class B:public A
{
public:
int x;
int y;
}
class C:public B
{
int hp;
}
这里B继承了A,C继承了B,C包括了A和B的所有成员属性及函数,B只包括了 A的属性及函数
这里如果继承语法里面的public不写,不写就相当于private
protected使用时机
当继承父类时,父类的private成员不能被子类访问,但是父类的private成员在子类的内存中还是存在,只是不能访问,而protected属性和private没有区别,唯一区别是在继承后,它的子类可以访问父类的protected成员
final关键字
当加上关键字后,这个类就不能被继承
public、protected等继承的区别
继承方式为public,则父类中的public成员被继承为public,protected被继承为protected,父类中的private被继承不可访问
继承方式为protected,则父类中的pulic成员被继承为protected,protected被继承为protected,private被继承不可以访问
继承方式为private,父类中的public和protected被继承为private,private成员被继承不可访问
如果我们想要修改子类继承父类的成员权限时,可以用using 父类::成员
class A
{
protected:
int x;
int y;
};
class B :public A
{
public:
using A::x;
};
int main()
{
B b;
b.x = 100;
A a;
a.x = 200;
}
比如这样,就修改了x在B中的权限,但是这段代码还是不能运行,因为x在A中,还是以proteced的特征存在
构造函数和继承
#include <iostream>
class Object
{
protected:
char Name[5]{};
public:
Object()
{
std::cout << "Object is created" << std::endl;
}
};
class seeObject:protected Object
{
public:
int x;
seeObject()
{
std::cout << "seeObject is created" << std::endl;
}
};
int main()
{
seeObject stone;
system("pause");
}
这里当实例化seeobject对象 stone时,会同时调用父类的构造函数,因为在子类中继承了父类的成员变量,所以实例化也要实现这个成员变量,从而调用默认构造,构造顺序是父类构造、子类构造,因为在seeobject类的内存中,父类的成员在前面。
#include <iostream>
class Object
{
protected:
char Name[5]{};
public:
Object()
{
std::cout << "Object is created" << std::endl;
}
Object(const Object& obj)
{
std::cout << "Object copy is created" << std::endl;
}
};
class seeObject:protected Object
{
public:
int x;
seeObject()
{
std::cout << "seeObject is created" << std::endl;
}
seeObject(const seeObject& seeobj)
{
std::cout << "seeObject copy is created" << std::endl;
}
};
class creature:protected seeObject
{
protected:
int hp;
public:
creature()
{
x = 3200;
std::cout << "creature is created" << std::endl;
}
creature(const creature& cret)
{
std::cout << "creature copy is created" << std::endl;
}
};
int main()
{
creature monkey;
creature monkey2 = monkey;
system("pause");
}
这里,子类进行复制构造时,不会调用父类的复制构造函数,子类实例化时都是调用父类的默认构造函数,不会调用其他构造,比如不会调用父类的复制构造,除非显示指定
为什么呢?
当进行复制构造的时候,按理说人类拥有了动物类的所有成员变量,当进行人类的对象复制构造时,那这个时候会调用人类的复制构造函数,动物类的复制构造会不会被调用呢?如果说人类实例化对象也属于动物类的话,那么动物类的复制构造也会被调用,
按照语法规定,不会调用动物类的复制构造,只会调用动物类的构造函数
=========================================================================
为什么会调用动物的普通构造呢?直接一点,直接调用人的复制构造不就好了吗?直接一次性全部打包复制,这是因为人的继承属性,属性必须初始化,而这个属性是属于动物类的,就会默认调用动物的默认构造进行初始化,人不会继承动物的默认构造,但是会调用它
==============================================================================
为什么不调用动物的复制构造呢?就好比,动物有血有肉,人有血有肉,动物的血肉值为100,人的血肉值为200,当复制人的时候,肯定是复制200的血肉值,所以肯定不是调用动物的复制构造,不然血肉值也成了100,只需要把人的继承属性调用动物的普通构造初始化,再加上人的特有属性,两个加起来再调用复制构造复制过去即可。
=========================================================================
为什么不调用人的普通构造呢?因为既然要复制一个人,所以就肯定会调用复制构造,当给人进行普通构造的时候,就好比,初始化一个人的血肉值为10,然后实例化后,将人的血肉值设置为200,那么当复制这个人的时候,肯定不会再调用这个人的普通构造,因为这样又将他设置成了10。所以调用了复制构造肯定不会再调用普通构造
=========================================================================
那么问题来了,按照前面所说,调用了动物的普通构造,将血肉值初识化成了100,只调用人的复制构造,不调用人的普通构造,那么在进行复制的时候,会不会把100也给复制过去呢?不会的,因为在创建这个要复制的对象时,已经给它进行了初始化血肉值为200,就好比要复制一块金子,这块金子早就已经点石成金了,不用担心复制一块石头过去。
=========================================================================
#include <iostream>
class seeObject
{
public:
int x;
seeObject()
{
std::cout << "seeObject is created" << std::endl;
}
seeObject(const seeObject& seeobj)
{
std::cout << "seeObject copy is created" << std::endl;
}
seeObject(int val) :x{ val }
{
}
};
class creature:protected seeObject
{
protected:
int hp;
public:
/using seeObject::seeObject;
creature():seeObject {100},hp{200}
{
x = 3200;
std::cout << "creature is created" << std::endl;
}
creature(const creature& cret):seeObject{cret}
{
std::cout << "creature copy is created" << std::endl;
}
};
int main()
{
creature monkey;
creature monkey2 = monkey;
system("pause");
}
这里,采用成员初始化列表的方式,将100传入seeObject(int val),注意,这里的x=3200,只能写在括号里面,而不能用初始化列表的方式,因为这里还未完成构造
当子类委托构造父类的复制构造函数时,传入的参数是子类类型,也可以,因为子类实例化对象类型也属于父类对象类型,比如人属于动物类,虽然子类多了自己的属性和方法,但它仍然符合父类的定义
==================================
知识扩展*:<1>.通常来说,子类肯定含有父类所有的成员变量和方法函数.所以用父类指针指向子类时,没有问题,因为父类有的,子类都有,不会出现非法访问问题.
<2>.如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量(基类是没有的),就会出现非法访问;
因为被子类指针指向的由父类创建的对象,根本没有要访问的那些内容,那些是子类特有的,只有用子类初始化对象时才会有
===================================
对于第23行,using seeObject::seeObject;这句代码是继承父类的有参构造函数,使用using继承构造函数是不能继承默认构造和副本构造,其他都可以继承,比如有参构造。继承下来的构造函数不改变它的属性,如果在父类中,它是public,那么继承后,如果把using seeObject::seeObject;这段代码放入creature的private中,它也是public属性。
继承构造时不能选择继承哪个有参构造,而是将所有有参构造全部继承下来
继承和析构
析构的顺序和构造相反,最先析构的是子类,然后才是父类,因为子类对父类有依赖,所以要先析构子类。当把子类中的原有成员销毁时,调用子类的析构,然后销毁继承的成员时,又会调用父类的析构