美文网首页
博览网:C++面向对象高级编程(下)第二周笔记

博览网:C++面向对象高级编程(下)第二周笔记

作者: 博览网小学员 | 来源:发表于2017-08-16 20:55 被阅读0次

    一、虚指正(vptr)和虚表(vtbl)

    我们以下图介绍上述两者:

    1、当类中存在虚函数就会出现虚指针vpt,无论虚函数有多少个,有且仅有一个虚函数,指向虚表(rvtbl)的地址;

    2、虚表是什么呢??

    我们可以将它理解为一种表格,每个表格的位置存放一个虚函数对应内存的地址;

    例如:基类A中包含两个虚函数vfunc1()、vfunc2(),那么类A的对象在在内存中表现如上图a(A object),其存储类的两个基本数据:m_data1、2m_data2以及两个虚函数对应的虚指针vptr,而虚指针指向虚表的地址,虚表存放虚函数内存的两个地址:0x401ED0、0x401FD0;同理,A类的子类B,B类的子类C也有类似的原理;

    将vptr实现vtbl内容翻译为C:

    (*p->vptr)n;

    (* p->vptr[n])(p);

    3、动态绑定: 虚机制

    动态绑定(dynamic binding):动态绑定是指在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。

    C++中,通过基类的引用或指针调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的;

    C++中动态绑定条件发生需要满足2个条件:

    (1)只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不能进行动态绑定

    (2)必须通过基类类型的引用或指针进行函数调用

    所谓的动态类型,当引用或指针调用了虚函数时,它就是动态类型,它的行为要到程序运行时才能定义 ;

    当我们用派生类去初始化基类的引用或指针后,假如调用的是非虚函数,那么这时实际调用的函数是基类的函数;假如调用的是虚函数,那么这是调用的是派生类自己定义的虚函数  下面是具体的例子来说明静态类型和动态类型

    class A{

    public:

    virtual void show(){cout<<"j基类的show()"<

    void get(){cout<<"基类的get()"<

    };

    class B:public A{

    public:

    virtual void show(){cout<<“派生类的show()”<

    void get(){cout<<"派生类的get()"<

    };

    main:

    A a;

    B b;

    A &c=b;

    c.show();//show函数是虚函数,并且此时使用派生类的对象去初始化基类的引用,发生了动态绑定,调用的是实际类        型B的show()----"派生类的show"

    c.get();//此时不满足动态绑定的条件,c是静态类型,结果是-------基类的get()

    二、this指针

    1、C++this指针,一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象。全局仅有一个this指针,当一个对象被创建时,this指针就存放指向对象数据的首地址;

    注:简单点说,通过一个对象调用函数时,函数的地址就是this;

    举例:

    父类CDocument

    子类CMyDoc,子类对虚函数Serialise()进行了重新定义;

    通过语句myDoc.OnFileOpen()调用父类函数时,子类对象myDoc的地址即为this,上述语句可表述为:CDocument::OnFileOpen(&myDoc),&myDoc就是this,this调用虚函数,进行动态绑定,通过this->Serialize()调用了子类的虚函数,而不是父类的虚函数,this->Serialize()也可以表达为虚指针、虚表的形式,即:*(this->vptr)[n](this),这样我们就可以更好的理解this以及虚机制;

    三.动态绑定

    再第一章节已介绍过动态绑定,这里就不再赘述;

    四、const

    课件中已做了详细的介绍,我这里简单总结下,并做些衍生:

    1、常数对象可以调用常函数;

    非常数对象可以调用常函数;

    常数对象不可以调用非常函数;

    非常数对象可以调用非常函数;

    注:当成员函数的常数版本和非常版本同时存在时(以函数重载形式出现),常数对象只可以调用常函数;非常数对象只可以调用非常函数。

    2、注意的几点:

    1)const一般放在成员函数后头,不放在全局函数后头, 例:void function() const { return data;} ;

    2)在成员函数后面加const是属于签名, 就是当两个成员函数传参相同,那么加不加const也会被区分成两个函数. ;

    3、const的其他使用方法:

    1)定义常量

    (1)const修饰变量,以下两种定义形式在本质上是一样的。

    它的含义是:const修饰的类型为TYPE的变量value是不可变的。

    TYPE const ValueName = value;

    const TYPE ValueName = value;

    (2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义.

    extend const int ValueName = value;

    2)指针使用CONST

    (1)指针本身是常量不可变

    char* const pContent;

    (2)指针所指向的内容是常量不可变

    const char *pContent;

    (3)两者都不可变

    const char* const pContent;

    (4)还有其中区别方法,沿着*号划一条线: 如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量; 如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

    五、关于New,Delete

    new对象的流程不能更改,但是实现过程中的函数可以被更改.

    operator new

    operator delete

    array new一定要array delete;

    回忆前边的内容:delete 某个对象,其实质是先调用析构函数,再释放内存

    六、重载::operator new, ::operator new[],::operator delete ,::operator delete[]

    在全局当中:

    Note: 如果你重载了全局的操作符, 所以要额外小心.

    这些重载不可以被声明在一个namespace中.

    //这里的函数是编译器去调用, 所以size是编译器给出.

    void* operator new( size_t size )

    { return malloc(size);}

    void* operator new[]( size_t size )

    { return malloc(size);}

    void* operator delete(void* ptr )

    { free(ptr);}

    void* operator delete[](void* ptr )

    { free(ptr);}

    重载 member new , delete

    在class里面重载new, delete

    class foo{

    public:

    void* operator new(size_t size);

    void operator delete(void *, size_t size); //size为可选

    …….

    };

    那么你在:

    foo *a = new foo;

    delete a;

    就会调用上面重载的函数.

    new[] , delete[] 也如此.

    七、实例

    当类中重载了new , delete , 而又想调用全局的new , delete

    可以这样写:

    ::delete a;

    string类内其实是一个指针.

    当创建一个数组的时候, 内存当中就会多分配一个指针,该指针用于保存当前数组个数.

    八、重载new(),delete()示例

    允许重载成员函数new(….) 其中参数中,必须有第一个且第一个必须是size_t size. 其余参数以new所指定的placement argument为初值.

    Foo* p = new(300,’c’)Foo; //这里是三个参数

    我们也可以重载类成员函数 operator delete() ,写出多个版本. 但他们绝不会被 通常所使用的delete调用.只有当new所调用的ctor抛出 异常,才会调用这些重载版的operator delete(). 它们只能这样被调用,主要用来归还未能完全创建成功的对象所占用的内存.

    九、Basic_String使用new(extra)扩充申请量

    Basic_String在重载new()过后,传递了一个extra参数, 用于后台自动多申请extra空间。

    相关文章

      网友评论

          本文标题:博览网:C++面向对象高级编程(下)第二周笔记

          本文链接:https://www.haomeiwen.com/subject/dyjwrxtx.html