C++继承

一、继承作用:进行代码复用,将重复的部分作为公共部分来使用,从而不必在构建每一个类的时候都将相同的部分在写一遍。通过继承,可以获得父类的所有功能,并且可以重写已有功能,添加新功能。

二、继承的规则与特点:

1. 子类对象在创建时调用父类的构造函数,然后在调用自己的构造函数;先基类构造再派生构造。
2. 子类可以直接使用父类的成员函数,也就是子类拥有父类的所有属性和行为。子类可以当做父类对象使用。 
3. 子类中可以添加父类没有的方法和属性。 
4. 子类对象可以直接赋值给父类对象(public继承),也就是说子类对象可以直接初始化父类对象。

eg: Base base;//基类

      Drive drive;//派生类

      base=drive;

5.当派生类中存在与基类同名的成员的时候(成员函数是否同名与参数无关),通过派生类的对象进行访问的时候,只能访问派生类中的成员,若要访问基类中的成员,必须加上作用域限定符(同名隐藏);

6.在基类中是虚函数,则派生类中同名且参数相同的函数也是虚函数。

7.基类对象的引用或指针可以指向派生类的对象,反之不可;(public继承)

       Base *pb=new Drive(10);    V

       Drive* pd=new Base(10);    X

8.友元函数是不能被继承的(友元函数不是成员函数);

9.基类中的静态成员可以被继承,但无论有多少派生类,该静态成员仅有一份,也就是说基类与派生类共用一份该静态成员;

三、基类不同的访问限定符的成员以不同的继承方式继承,在派生类中的访问限定

四、构造函数和析构函数

基类的构造,析构函数派生类无法继承下来,必须在派生类构造函数的初始化列表上表明基类的构造方式。

析构:要将基类的析构设置为虚函数,才能防止内存泄露,举个例子:

class Base
{
public:
	Base(int a) :ma(a)
	{
		cout << "Base::Base()" << endl;
	}
    void show()
    {
        cout << "Derive::show()" << endl;
    }
	virtual ~Base()
	{
		cout << "Base::~Base()" << endl;
	}
	
protected:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int b) :mb(b), Base(b)
	{
		cout << "Derive::Derive()" << endl;
	}
    virtual void show()
    {
        cout << "Derive::show()" << endl;
    }
	~Derive()
	{
		cout << "Derive::~Derive()" << endl;
	}
	
private:
	int mb;
};
int main()
{
	Base* pb = new Derive(10);
	pb->Show();

	delete pb;//会崩溃
	
	return 0;
}

如果基类析构不设置成虚函数,则派生内存布局为下图,造成内存泄露

如果基类析构设置成虚函数,(动态绑定)则派生内存布局为下图,(把派生类的vfptr合并到基类的vfptr中)

指针调用虚函数是动多态,发生动态绑定,运行时确定函数的调用;

编译期间确定虚函数表,从表里拿到虚函数入口地址,存放到eax中(call  eax),虚表存放在.rodata只读数据段,(运行时会将.data和.text段加载到内存中),所以运行时会通过两个logal加载器把虚表加载到内存中,运行时就可以知道虚函数的入口地址。这样,就会先析构派生类,后析构基类。

五、同名函数关系

1、重载(overlode):

相同作用域;函数名相同;参数列表不同(参数类型不同,或者参数个数不同,或者参数个数和参数类型都不相同);返回类型随意。
 产生原因:主要是因为在C++中,编译器在编译.cpp文件中当前作用域的同名函数时,函数生成的符号由返回值类型(不起决定作用)+形参类型和顺序(起决定作用)的组成

作用:用同一个函数名命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

2、隐藏(overhide)/ 就是重定义:(存在,看不见)

作用域不同(分别在父类和子类中);函数名相同;除过覆盖的同名函数都是隐藏关系。
派生类的同名函数隐藏了基类中所有的同名函数。
3、覆盖(override)/  就是重写:(不存在)
覆盖的作用:实现动多态。
发生条件:
1)作用域不同;函数名相同;参数列表相同;返回类型相同;
2)发生在虚表中,基类函数的同名函数是虚函数;
3)派生类中的同名且参数列表相同的函数覆盖了基类同名,参数列表相同的函数

在子类中定义了一个与父类完全相同的函数时,称子类这个函数覆盖了父类的这个虚函数。
完全相同代表着两个函数的函数名、参数个数、参数类型、返回值类型都相同;也有特殊例子(协变)。

**协变:子类的虚函数和父类中的虚函数的函数名、参数个数、参数类型都相同,只是返回值类型不同,

父类的虚函数返回的是父类的指针或者引用,Base:         virtual  Base& show();
子类的虚函数返回的是子类的指针或者引用,Drive:         virtnal  Drive& show();

这种情况下也会产生子类的虚函数覆盖父类的虚函数(返回值类型不同)

注:派生类的析构会覆盖掉基类的析构

六、赋值兼容规则

子类对象可以直接赋值给父类对象(public继承),也就是说子类对象可以直接初始化父类对象。

eg: Base base;//基类

      Drive drive;//派生类

      base=drive;

七、has_a  /  is_a的关系

组合关系:has_a  <--------->   a   part   of

               节点和链表的关系,节点是链表的一部分

继承关系:is_a<-------->   a   kind    of

              学生和人的关系,学生是人的一种

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值