• 初步学习C++中的继承关系


    继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能

    这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

    #include<iostream>
    using namespace std;
    class Base
    {
    public:
    	Base()
        {}
    	~Base()
    	{}
    private:
    	int _pri;
    protected:
    	int _pro;
    public:
    	int _pub;
    };
    class Derive : public Base
    {
    public:
    	Derive()
    	{}
    	~Derive()
    	{}
    private:
    	int _dPri;
    protected:
    	int _dPro;
    public:
    	int _dPub;
    };
    int main()
    {
    	cout << sizeof(Base) << endl;//12=4+4+4
    	cout << sizeof(Derive) << endl;//24,派生类的大小包括了基类的大小和自己的大小
    	return 0;
    }

    继承关系和訪问限定符:

    class Base
    {
    public:
    	Base()
    	{}
    	~Base()
    	{}
    private:
    	int _pri;
    protected:
    	int _pro;
    public:
    	int _pub;
    };
    class Derive :public Base
    {
    public:
    	Derive()
    	{}
    	~Derive()
    	{}
    	void Display()
    	{
    		cout << "_pri=" << _pri << endl;//error基类的私有成员在派生类中不能被訪问
    		cout << "_pri=" << _pro << endl;
    		cout << "_pri=" << _pub << endl;
    		cout << "_dpri=" << _dpri << endl;
    		cout << "_dpri=" << _dpri << endl;
    		cout << "_dkpri=" << _dpri << endl;
    	}
    private:
    	int _dpri;
    protected:
    	int _dpro;
    public:
    	int _dpub;
    };
    int main()
    {
    	Derive d;
    	d._pri = 10;//error
    	d._pro = 20;//error

    	d._pub = 30;
    	d._dpri = 20;//error
    	d._dpro = 20;//error
    	d._dpub = 20;
    }
    总结:
    1.基类的private成员在派生类中是不能被訪问的。假设基类成员不想在类外直接被訪问,但须要在派生类中能訪问。就定义为protected。能够看出保护成员限定符是因继承才出现的。
    2.public继承是一个接口继承。保持is-a原则,每一个父类可用的成员对子类也可用,由于每一个子类对象也都是一个父类对象。
    3.protetced/private继承是一个实现继承。基类的部分成员并不是全然成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系。在绝大多数的场景下使用的都是公有继承。


    4.无论是哪种继承方式,在派生类内部都能够訪问基类的公有成员和保护成员。基类的私有成员存在可是在子类中不可见(不能訪问)。


    5.使用keywordclass时默认的继承方式是private。使用struct时默认的继承方式是public。只是最好显示的写出继承方式。
    6.在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承.

    在派生类中假设没有显示定义类的六个成员函数,编译器系统则会默认合成这六个默认的成员函数

    class Base
    {
    public:
    	Base()
    	{
    		cout << "Base()" << endl;
    	}
    	~Base()
    	{
    		cout << "~Base()" << endl;
    	}
    private:
    	int _pri;
    protected:
    	int _pro;
    public:
    	int _pub;
    };
    class Derive :public Base
    {
    public:
    	Derive()
    	{
    		cout << "Derive()" << endl;
    	}
    	~Derive()
    	{
    		cout << "~Derive()" << endl;
    	}
    private:
    	int _dpri;
    protected:
    	int _dpro;
    public:
    	int _dpub;
    };
    int main()
    {
    	{
    		Derive d;
    	}
    	getchar();
    	return 0;
    }

    继承关系中构造函数调用的顺序:基类构造函数 =》派生类中对象构造函数 =》派生类构造函数体

    继承关系中析构函数调用的顺序:派生类析构函数 =》派生类包括成员对象析构函数 =》基类析构函数

    说明:  1、基类没有缺省构造函数。派生类必需要在初始化列表中显式给出基类名和參数列表。
            2、基类未定义构造函数。则派生类也能够不用定义,所有使用缺省构造函数。
            3、基类定义了带有形參表构造函数。派生类就一定定义构造函数。

    默认构造函数:指调用时无需提供參数的构造函数。因此:它能够是没有參数的,也能够是全部參数都有默认值的,两者取其一,否则有二义性。

    编译器会合成默认(缺省)构造函数:1.当类中有类类型的成员,且类成员有默认的构造函数。

                                    2.当基类有默认构造函数时,派生类没有构造函数;

                                    3.当类中有虚函数或类派生链中有虚函数时;

                                    4.当类採用虚继承方式时。


    调用拷贝构造函数:1.用一个对象初始化还有一个对象时,且是同类对象。(对象构建对象)

                      2.当函数的參数是对象。通过对象调用函数时,參数的拷贝会调用拷贝构造函数;(值传參)

                      3.当函数的返回值是对象,调用函数时。返回一个对象会调用拷贝构造函数。

    (值返回值)

    编译器会合成拷贝构造函数1.当类中有类类型的成员,且类成员有拷贝构造函数;

                              2.当基类有拷贝构造函数时。派生类没有拷贝构造函数。

                              3.当类中有虚函数时;

                              4.当类採用虚继承方式时。


    继承体系中的作用域:

    1.在继承体系中基类和派生类是两个不同作用域。
    2.子类和父类中有同名成员。子类成员将屏蔽父类对成员的直接訪问。(在子类成员函数中。能够使用 基类::基类成员 訪问)--隐藏 --重定义
    3.注意在实际中在继承体系里面最好不要定义同名的成员。

    继承与转换--赋值兼容规则--public继承:1.子类对象能够赋值给父类对象(分割/切片)
                                         2.父类对象不能赋值给子类对象
                                         3.父类的指针/引用能够指向子类对象
                                         4.子类的指针/引用不能指向父类对象(能够通过强制类型转换完毕)

    class Base
    {
    public:
    	Base(int data)
    	{}
    	~Base()
    	{}
    private:
    	int _pri;
    protected:
    	int _pro;
    public:
    	int _pub;
    
    	static int count;
    };
    
    int Base::count = 0;
    class Derive : public Base
    {
    public:
    	Derive()
    		: Base(20)
    	{}
    
    	~Derive()
    	{}
    private:
    	int _dPri;
    protected:
    	int _dPro;
    public:
    	int _dPub;
    	int _pri;
    };
    int main()
    {
    Derive d;
    Base b(0);
    b = d;
    d = b;//error,父类对象不能赋值给子类
    Base* pBase = &d;
    Derive *pD = (Derive*)&b;//通过强制类型转换能够赋值
    return 0;
    }


    友元与继承:友元关系不能继承,也就是说基类友元不能訪问子类私有和保护成员。
    继承与静态成员:基类定义了static成员,则整个继承体系里面仅仅有一个这种成员。不管派生出多少个子类。都仅仅有一个static成员实例。

    单继承:一个子类仅仅有一个直接父类。

    多继承:一个子类至少有两个父类。

    菱形继承:会产生二义性和数据冗余问题。

    class Person
    {
    public:
    	string _name;   // 姓名
    };
    class Student : public Person
    {
    protected:
    	int _num;   //学号
    };
    class Teacher : public Person
    {
    protected:
    	int _id;     // 职工编号
    };
    class Assistant : public Student, public Teacher
    {
    protected:
    	string _majorCourse;     // 主修课程
    };
    void Test()
    {
    	// 显示指定訪问哪个父类的成员
    	Assistant a;
    	a.Student::_name = "xxx";
    	a.Teacher::_name = "yyy";
    }
    为了解决菱形继承的二义性和数据冗余问题。使用虚继承
    #include<iostream>
    using namespace std;
    class Base
    {
    public:
            virtual void FunTest1()  //虚函数
    	{
    		cout << "Bsae::FunTest1()" << endl;
    	}
    	virtual void FunTest2()  //虚函数
    	{
    		cout << "Base::FunTest2()" << endl;
    	}
    	int _data1=0x01;
    };
    class Derive :virtual public Base  //虚继承
    {
    public:
    	virtual void FunTest3()  //虚函数
    	{
    		cout << "Derive::FunTest3()" << endl;
    	}
    	virtual void FunTest4()  //虚函数
    	{
    		cout << "Derive::FunTest4()" << endl;
    	}
    	int _data2=0x02;
    };
    int main()
    {
    	Base b1;
    	Derive d1;
    	return 0;
    }
    <img src="http://img.blog.csdn.net/20160413123747132?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
    对象d1中存储了两个虚指针:各自是指向base的虚指针_vfptr和指向Derive的虚指针_vfptr。

    <img src="http://img.blog.csdn.net/20160413123806697?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
    0x00cfddf8表示的是Derive表自己的地址
    0x00cfdbb0表示的是派生类独立成员相对于Base类成员偏移的地址
    0x00cfdca8表示Derive中属于Base类的那部分的虚表地址
    </pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_18_7825472">在base的虚指针中存放的是base中虚表的地址。通过虚指针能够找到虚表的地址,即虚表中存放的是成员函数的调用地址:
    <img src="http://img.blog.csdn.net/20160413115724037?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
    </pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_21_4368718">相同的在Derive中的虚指针中存放了Derive的虚表地址:
    <img src="http://img.blog.csdn.net/20160413120037523?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
    </pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_24_9275761">总结:
    <span style="color:#ff0000;">虚拟继承下,派生类对象组成:</span>
    <span style="color:#ff0000;">1.派生类自己的虚表指针+派生类第一个成员相对于基类成员的偏移+派生类自己的数据部分</span>
    <span style="color:#ff0000;">2.基类的虚表指针+基类的数据部分</span>

    
    

  • 相关阅读:
    一个BUG显示了IE7和IE6解析URL中中文参数能力的不同
    Winform+Webservice小结
    精通 JS正则表达式
    JSON
    Jquery
    MongoDB 驱动实践
    概要设计(总体设计)
    LINQ 学习
    MongoDB培训
    数据流程图(需求分析方法和建模工具)
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7169962.html
Copyright © 2020-2023  润新知