C++继承

类与类之间的关系

  • 嵌套:一个类中声明了另一个类------一个类是另一个类的一部分
class B
{
public:
    class A
    {};
    A a;
};
//访问A时要通过B的作用域
  • 代理:一个类的接口是另一个类的子集------一个类的功能需要依赖另一个类的功能实现
  • 继承:一个类是另一个类的一种
class Fish
{
public:
    string _name;
};

class GoldFish:public Fish //继承
{
public:
    string _color;
};

int main()
{
    Fish fish;
    fish._name = "yu";
    
    GoldFish gfish;
    gfish._name = "jinyu";
    glish._color = "gold";

    return 0;
}

继承

  • 父类的私有成员不可以被子类所继承
  • 子类中先构造父类,再构造子类,先析构子类,再析构父类
  • 如果构造函数需要传参的话,必须将参数放在初始化列表中
  • 父类的构造如果需要传参,就必须要写到初始化列表中
  • 子类从父类继承来的成员的属性不可能超过继承的属性,eg:子类protected继承父类,则继承来的父类的成员中不可能有共有的属性
  • 公有继承,父类的公有成员放在子类的公有成员中,保护成员放在子类的保护成员中,父类的私有成员不可以被继承
  • 保护继承,父类的公有成员和保护成员放在子类的保护成员中,私有成员不能被继承
  • 私有继承,父类的公有成员与保护成员放在了子类的私有成员中
  • 在类外访问的时候,类内的私有成员和保护成员不可以被访问,公有成员可以被访问,相当于对外的接口
  • 在类内访问的时候,类内的公有成员可以访问私有成员与保护成员
class Person
{
public:
    void eat();
    {
        cout<<"eat ..."<<endl;
    }
    void work();
    {
        cout<<"work ..."<<endl;
    }

protected:
private:
    string _name;
    int _age;
    string _sex;
};

class Student:public Person
{
public:
    void show()
    {
        cout<<"name:"<<_name<<endl;//报错,子类无法访问父类的私有成员,将父类的这三个成员属性放到protected中就没有问题了
        cout<<"age:"<<_age<<endl;//报错
        cour<<"sex:"<<_sex<<endl;//报错
        cout<<"num:"<<_num<<endl;
    }
protected:
private:
    string _num;
}
#include<iostream>
using namespace std;
class Person
{
public:
	Person(string name, int age, string sex, string wife = string())
	{
		_name = name;
		_age = age;
		_sex = sex;
	}
	void eat()
	{
		cout << "eat......" << endl;
	}
protected:
	string _name;
	int _age;
	string _sex;
private:
	string wife;//私有成员不能被继承
};

class Student :private Person//私有继承后,父类的公有保护成员全部继承到子类的私有成员当中,在子类的公有函数当中,可以访问这些私有的成员,但是在类外不可以访问
{
public:
	Student(string name, int age, string sex, string num, string wife = string())
		:Person(name,age,sex,wife)//引用、成员对象需要传参和父类的构造如果需要传参,就必须要写到初始化列表中
	{
		_num = num;
	}
protected:
private:
	string _num;
};
int main()
{
	Student s("zhangsan", 21, "nan", "19010101");
	return 0;
}
  • 隐藏:派生类会隐藏父类的同名成员------同名隐藏 ,访问父类被隐藏的成员需要加上父类的作用域
class Base
{
public:
    void fun1()
    { 
        cout << "Base::void fun1()" <<endl;
    }
    void fun2(int a)
    {
        cout << "Base::void fun2(int a)" << endl;
    }
private:
    int _a;
};

class Derive : public Base
{
public:
    void fun1()
    {
        cout << "Derive::void fun1()" <<endl;
    }
    void fun1(int a)
    {
        cout << "Derive::void fun1(int a)" <<endl;
    }
    void fun2()
    {
        fun2(10);//依然会报错
        cout << "Derive::void fun2()" << endl;
    }
     void fun2(int a,int b)
    {
        cout << "Derive::void fun2(int a,int b)" << endl;
    }
private:
    int _b;
};

int main()
{ 
    Derive d;
    d.fun1();//调用的是子类的fun1()
    d.Base::fun1();//调用基类的fun1要加作用域
    d.fun2(10);//会报错,为什么呢? //隐藏,调用父类被隐藏的成员时要加作用域
    return 0;
}
  • 构造与析构顺序: 先构造父类,在构造子类,先析构子类,再析构父类
  • 父类指针指向子类对象,可以直接赋值,子类指针不可以指向父类对象
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base()" << endl;
    }
    void fun1(int a)
    { 
        cout << "Base::void fun1(int a)" <<endl;
    }
private:
    int _a;
};

class Derive : public Base
{
public:
    Derive()
    {
        cout << "Derive()" << endl;
    }
    ~Derive()
    {
        cout << "~Derive()" << endl;
    }
private:
    int _b;
};

int main()
{ 
    Derive d;//构造顺序,先构造父类,在构造子类,先析构子类,再析构父类
    Base* pb = new Derive();//父类指针指向子类对象,可以直接赋值
    pb->fun1(10);
    delete pb; //在析构的时候没有析构子类的对象,为什么?
               //因为编译器根据类型直接调用该类型的析构
               //子类的析构函数调用完后,会自动调用父类的析构,而父类的并不会
               //如何解决这一问题?虚函数virtual
    return 0;
}

动多态

动多态的产生

使用指针或者引用调用虚函数,就会产生动多态

动多态的调用过程

  1. 使用指针或者引用调用虚函数
  2. 在对象中找到vfptr
  3. 根据vfptr找到vftable
  4. 在vftbale中找到要调用的函数
  5. 调用

vftable的产生时间

  1. 编译时期如果发现类中有虚函数,就会对这个类生成vftable(虚函数表)
  2. 将当前虚函数的指针放到vftable中
  3. 补充:vftable在编译时期会被放到只读数据段(.rodata)

vfptr什么时候有指向

  1. 当对象构造的时候,如果发现该类有vftable,就会将vftable的地址写入到这个对象之中

静多态的整个过程

  1. 父类先编译,如果遇到虚函数,则在数据段创建虚函数表,并且将虚函数的指针加进去
  2. 编译子类时,子类继承父类的虚函数表,若有同名的虚函数,则覆盖虚函数表中父类的虚函数,若子类中有新的虚函数,则加入这个虚函数表中
  3. 创建一个对象时,看到有虚函数表,则会自动生成一个指向虚函数表的成员指针vfptr,然后将虚函数表的指针赋值给vfptr
Base* pb = new Derive();//创建一个子类的对象,pb这个指针指向的是Derive这个对象

delete pb; //通过指针调用析构函数,此时的析构函数是虚函数,就产生了动多态的调用,
           //指针pb先找到vfpte这个成员,再通过vfptr找到vftable,然后再vftable中找到析构函数
           //子类函数的虚构函数调用完后,会自动调用父类的虚构函数

虚函数

  • 虚函数具有传递性
  • 父类中如果有虚函数,那么子类中对应的相同的函数会被传递为虚函数
  • 相同的函数:同返回值,同参数名,同参数列表
  • 子类的析构函数与父类的析构函数是相同的函数,都叫“析构函数”,所以父类的析构函数的可以传递给子类

覆盖(重写)

  • 覆盖发生的地点:vftable
  • 覆盖的原因:父类中的虚函数会被子类中相同而函数覆盖-------在子类的需函数表中覆盖的

重载,隐藏于覆盖三者之间的区别

  • 重载------函数名相同,参数列表不同
  • 隐藏------子类当中如果有与父类中同名的函数,则父类中同名函数会被隐藏,如果需要使用,则需要加作用域
  • 覆盖------相同的函数如果在父类中加了vitual关键字,则在子类当中会将父类的同名函数覆盖
  • 覆盖的函数一定会存在隐藏关系
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值