栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++杂讲 继承 与 派生

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++杂讲 继承 与 派生

目录

知识点1【继承和派生的概述】

知识点2【继承的格式】

知识点3【继承的内层结构】

知识点4【继承中的构造和析构的顺序】

知识点5【子类中 有父类、对象成员 构造和析构的顺序】

知识点6【子类中的构造】

知识点7【父类和子类的 同名成员变量 处理】

知识点8【父类和子类的 同名成员函数 处理】

知识点9【继承中的静态成员特性】

知识点10【多继承】(了解)

知识点11【菱形继承】 具有公共祖先 的多继承

知识点12【虚继承】

知识点1【继承和派生的概述】

A类继承于B类,A就直接拥有B类的资源

继承的优点:减少代码的冗余 提高代码的重用性

知识点2【继承的格式】
派生类定义格式:
   Class 派生类名 :  继承方式 基类名{
         //派生类新增的数据成员和成员函数
   };
   class 子类: 继承方式  父类名{
       //子类新增的数据成员和成员函数
   };

继承方式分类:

        public : 公有继承  

        private : 私有继承   

        protected : 保护继承

父类个数分类:

        单继承:指每个派生类只直接继承了一个基类的特征   (一个父类 派生出 一个子类)

        多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特征(多个父类 派生出 一个子类)

注意:

        子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在子类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。

案例1:公有继承 public

//设置一个父类
class Base
{
public:
    int a;
private:
    int b;
protected:
    int c;
};

//设置一个子类
class Son:public Base
{
public:
    //父类中的public数据  在子类中 也是public
    //父类中的private数据 在子类中 是不可见的
    //父类中的protected数据 在子类中 是protected的
    //子类的内部
    void showSon()
    {
        //b = 200;//不能直接访问
        c =300;//在子类 内部是可以访问的
    }
};

void test01()
{
    //子类的外部
    Son ob;
    ob.a = 100;
    cout<<"父类中的public数据a = "< 

总结:

        父类中的public数据 在子类中 也是public

        父类中的private数据 在子类中 是不可见的

        父类中的protected数据 在子类中 是protected的

        (public 继承 父类中的私有数据 在子类 不可见 其他保持原样)

案例2:保护继承protected

//保护继承
class Son1:protected Base
{
private:

public:
    //父类中的public数据  在子类中 也是protected
    //父类中的private数据 在子类中 是不可见的
    //父类中的protected数据 在子类中 是protected的
    //子类的内部
    void showbase(){
        a = 100;//子类内部可访问
        //b = 200;//不能直接访问
        c = 300;//子类内部可访问
    }
};
void test02()
{
    Son1 ob;
    //ob.a;//子类外不可访问
    //ob.b;//子类外不可访问
    //ob.c;//子类外不可访问
}

总结: protected继承

        父类中的public数据 在子类中 也是protected

        父类中的private数据 在子类中 是不可见的

        父类中的protected数据 在子类中 是protected的

        (保护继承 父类的私有数据 在子类中 不可见 其他数据 都变保护)

案例3:私有继承 private

//保护继承
class Son2:private Base
{
private:

public:
    //父类中的public数据  在子类中 也是private
    //父类中的private数据 在子类中 是不可见的
    //父类中的protected数据 在子类中 是private的
    //子类的内部
    void showbase(){
        a = 100;//子类内部可访问
        //b = 200;//不能直接访问
        c = 300;//子类内部可访问
    }
};
void test03()
{
    Son2 ob;
    //ob.a;//子类外不可访问
    //ob.b;//子类外不可访问
    //ob.c;//子类外不可访问
}

总结:private继承

        父类中的public数据 在子类中 也是private

        父类中的private数据 在子类中 是不可见的

        父类中的protected数据 在子类中 是private的

(私有继承方式:父类的权限 在子类中 变私有)

继承方式总结:public(主要)、protected、private

 不管啥继承方式:父类中的私有数据在 子类中不可见

 知识点3【继承的内层结构】
class Base
{
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Son :public Base
{
public:
    int d;
    int e;
};
int main(int argc, char* argv[])
{
    cout << sizeof(Son) << endl;
    return 0;
}

Son类的布局:

 知识点4【继承中的构造和析构的顺序】
class Base
{
public:
    Base()
    {
        cout<<"父类的无参构造函数"< 

运行结果:

 总结:

        构造顺序: 父类(基类)构造 ------> 子类(派生类)构造

        析构顺序:子类(派生类)析构------> 父类 (基类) 析构

 

 知识点5【子类中 有父类、对象成员 构造和析构的顺序】

父类的构造和析构 对象成员的构造和析构 子类自身的构造和析构

总结:(重要)

class Other
{
public:
    Other()
    {
        cout<<"对象成员的构造函数"< 

运行结果:

 知识点6【子类中的构造】

1、子类会默认调用 父类的 无参构造

2、子类 必须 显式使用初始化列表 调用 父类的有参构造

调用形式:父类名称。

Son(int a,int b):Base(a),b(b)
{
    //this->b = b;
}
class Base
{
private:
    int a;
public:

    Base()
    {
        cout<<"父类的无参构造函数"<a = a;
        cout<<"父类的有参构造函数"<b = b;
        cout<<"子类的有参构造函数int"<b = b;
        cout<<"子类的有参构造函数 int int"< 

运行结果:

案例:

父类有参构造:

Base(int a, int data)
{
    this->a = a;
    this->data = data;
    cout<<"父类的有参构造函数"< 

子类想调用 父类有参构造:

//子类必须用 初始化列表 显示的调用父类的有参构造
//父类名称(参数)
Son(int a,int b, int c):Base(a,c),b(b)//显示的调用父类的有参构造
{
    //this->b = b;
    cout<<"子类的有参构造函数 int int"< 

知识点7【父类和子类的 同名成员变量 处理】

1、当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员

2、如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域。

class Base
{
    //父类的私有数据 一旦涉及继承 在子类中不可见
public:
    int num;
public:
    Base(int num)
    {
        this->num = num;
        cout<<"Base有参构造int"<num = num2;
        cout<<"有参构造int int"<class Base
{

private:
    int num;//父类的私有数据 一旦涉及继承 在子类中不可见
public:
    Base(int num)
    {
        this->num = num;
        cout<<"Base有参构造int"<num = num2;
        cout<<"有参构造int int"< 知识点8【父类和子类的 同名成员函数 处理】 

案例1:子类继承父类所有成员函数 和成员变量

class Base
{
public:
    void func(void)
    {
        cout<<"父类中的void func"<class Base
{
public:
    void func(void)
    {
        cout<<"父类中的void func"< 知识点9【继承中的静态成员特性】 
class Base
{
public:
    //静态成员属于类 而不属于对象
    static int num;
    static int data;

    static void showData(void);

};
int Base::num  = 100;
int Base::data = 200;

class Son:public Base
{
public:
    static int data;//父和子类 静态成员 同名
    static void showData(void);
};
int Son::data = 300;

void test01()
{
    //从Base类中访问
    cout< 

运行结果:

知识点10【多继承】(了解)

多继承的格式:

class 子类: 继承方式1 父类名1,继承方式2 父类名2,继承方式3 父类名3,....
{

};
//表示子类 是由 父类名1,父类名2,父类名3...共同派生出来
class Base1
{
public:
    int a;
};
class Base2
{
public:
    int b;
};

class Son:public Base1,public Base2
{
    //Son类 拥有了a  b
};
int main(int argc, char *argv[])
{
    Son ob;
    ob.a = 100;
    ob.b = 200;
    return 0;
}

多继承容易产生二义性: (解决办法1 使用作用域)

class Base1
{
public:
    int a;
};
class Base2
{
public:
    int a;
};

class Son:public Base1,public Base2
{

};
int main(int argc, char *argv[])
{
    Son ob;
    //ob.a = 100;//err Base1 和 Base2中都有a成员同名
    //解决办法:加作用域
    ob.Base1::a = 100;
    ob.Base2::a = 200;
    return 0;
}

知识点11【菱形继承】 具有公共祖先 的多继承
class Animal
{
public:
    int data;
};

class Sheep:public Animal
{
public:
};
class Tuo:public Animal
{
public:
};

class SheepTuo:public Sheep,public Tuo
{
public:
};
int main(int argc, char *argv[])
{
    SheepTuo st;
    //SheepTuo 从Sheep中继承data  从Tuo继承data 就产生二义性
    //st.data = 200;//err
    //第一中方式:加作用域解决
    st.Sheep::data = 200;
    st.Tuo::data = 300;

    return 0;
}

结构:

class Animal
{
public:
    int data;
};

class Sheep:public Animal
{
public:
};

class Tuo:public Animal
{
public:
};

class SheepTuo:public Sheep,public Tuo
{
public:
};

 知识点12【虚继承】

为解决上述问题,引出虚继承

virtual修饰继承方式

//继承的动作 虚继承
//父类:虚基类
class 子类:virtual public 父类
{
};

虚继承:

class Animal
{
public:
    int data;
};

class Sheep:virtual public Animal
{
public:
};

vbptr(虚基类指针) 其中v是virtual 虚 b是base 基类 prt指针(vbptr指向虚基类表)

vbtable(虚基类表) 保存了当前的虚指针相对于虚基类的首地址的偏移量

class Tuo:virtual public Animal
{
public:
};

 总结:之所以 产生 vbptr和vbtable 目的 保证 不管多少个继承 虚基类的数据只有一份。

class SheepTuo:public Sheep,public Tuo
{
public:
};

 

 案例:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
using namespace std;

class Animal
{
public:
    int data;
};

class Sheep :virtual public Animal
{
public:
};
class Tuo :virtual public Animal
{
public:
};

class SheepTuo :public Sheep, public Tuo
{
public:
};
int main(int argc, char* argv[])
{
    SheepTuo 2;
    st.data = 200;
    
    //通过Sheep的vbptr 寻找vbptr距离虚基类首地址的偏移量 
    //&st == vbptr
    //*(int *)&st sheep 的虚基类表的起始位置
    int off_set = (int)*((int*)(*(int*)&st) + 1);
    cout << off_set << endl;

    //通过sheep的vbptr 和 off_set定位虚基类的首地址
    cout << ((Animal*)((char*)&st + off_set))->data << endl;

    return 0;
}

注意:  

        虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的。

虚继承:不管继承多少次 虚基类 只有一份数据。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/947364.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号