美文网首页
【Boolan】第五周笔记

【Boolan】第五周笔记

作者: 听说名字长一点不会重复 | 来源:发表于2017-11-18 23:32 被阅读0次

    1.关于vptr和vtbl
    之前的学习已经了解到c++多态主要靠虚函数实现,如果说c++的class在实现上相比c的struct有什么开销的话,那么虚函数表(vtbl)的维护和每个对象实例里虚表指针(vptr)将是比较明显的开销。
    对于如下三个类

    class A {  
    public:  
        virtual void vfunc1() { cout << "A::vfunc1\n"; }  
        virtual void vfunc2() { cout << "A::vfunc2\n"; }  
        void func1() { cout << "A::func1\n"; }  
        void func2() { cout << "A::func2\n"; }  
    };  
      
    class B : public A {  
    public:  
        virtual void vfunc1() { cout << "B::vfunc1\n"; }  
        void funcb() { cout << "B::funcb\n"; }  
    };  
      
    class C : public B {  
    public:  
        virtual void vfunc1() { cout << "C::vfunc1\n"; }  
        void funcc() { cout << "C::funcc\n"; }  
    };  
    

    非虚成员函数:A::func1(),A::func2(),B::funcb(),C::funcc()会单独在内存里存一份
    虚成员函数:A::vfunc1(),A::vfunc2(),B::vfunc1(),C::vfunc1()也会单独存一份,但是这四个虚函数会由虚函数表来记录,由于这个例子里有三个类,因此内存里会有三份虚函数B::vfunc1(),A::vfunc2(),表,我们假设它们为A,B,C表。 A表里会有两个指针,分别指向A::vfunc1(),A::vfunc2()的地址,B表里两个指针,分别指向B::vfunc1(),A::vfunc2(),同理,C表里的指针指向C::vfunc1(),A::vfunc2()。
    对于用基类指针new子类的情况:A pa = new B; 这个实例对象里放的也是B类对应的虚函数表,因为编译器做了个向上转型(upcasting)。
    其实理解了虚函数表在内存的形式后,调用虚函数的代码可以这么表示: (
    (pa->vptr)[n])(pa) 因为第一个参数肯定是*this。

    2.动态绑定
    为了C++的多态性,是有动态绑定和静态绑定这两种说法的:
    静态绑定:绑定的对象是静态类型,也就是编译期就能决定的,是确定的,不会更改的,比如 A a; a的内容虽然会在运行期发生改变,但是a就是a,这点是不会变的。
    动态绑定:绑定的对象是动态类型,动态类型就是指在编译期无法决定的,因为它可能在运行期发生改变,比如指针:A* pa; pa可以在运行时重新指向其他对象,或者转型指向B类或者C类。

    3.const
    const对象不能调用non-const函数,只能调用const函数。

    4.关于new,delete和重载
    new和delete也是常用的,新申明的数组需要使用内存时,需要new一段size大小的空间,当使用结束时delete掉,即释放内存,他俩是配合着使用的,不能忘写其中一个。

    全局的new有六种重载形式:
    void *operator new(std::size_t count)
    
    throw(std::bad_alloc);             //一般的版本
    
    void *operator new(std::size_t count,  //兼容早版本的new
    
    const std::nothrow_t&) throw();    //内存分配失败不会抛出异常
    
    void *operator new(std::size_t count, void *ptr) throw();
    
    //placement版本
    
    void *operator new[](std::size_t count)  //
    
    throw(std::bad_alloc);
    
    void *operator new[](std::size_t count,  //
    
    const std::nothrow_t&) throw();
    
    void *operator new[](std::size_t count, void *ptr) throw();
    

    5.C++的各种new简介

    1.new T
    第一种new最简单,调用类的(如果重载了的话)或者全局的operator new分配空间,然后用类型后面列的参数来调用构造函数
    2. new T[]
    这种new用来创建一个动态的对象数组,他会调用对象的operator new[]来分配内存(如果没有则调用operator new,搜索顺序同上),然后调用对象的默认构造函数初始化每个对象
    3.new()T 和new() T[]
    这是个带参数的new,这种形式的new会调用operator new(size_t,OtherType)来分配内存
    这里的OtherType要和new括号里的参数的类型兼容,这种语法通常用来在某个特定的地址构件对象,称为placement new,前提是operator new(size_t,void*)已经定义,通常编译器已经提供了一个实现,包含头文件即可,这个实现只是简单的把参数的指定的地址返回,因而new()运算符就会在括号里的地址上创建对象.需要说明的是,第二个参数不是一定要是void*,可以识别的合法类型,这时候由C++的重载机制来决定调用那个operator new.当然,我们可以提供自己的operator new(size_,Type),来决定new的行为
    4. operator new(size_t)
    这个的运算符分配参数指定大小的内存并返回首地址,可以为自定义的类重载这个运算符,方法就是在类里面声明加上void *operator new(size_t size)
    5.operator new[](size_t)
    这个也是分配内存,,只不过是专门针对数组,也就是new T[]这种形式,当然,需要时可以显式调用
    6.operator new(size_t size, OtherType other_value)和operator new[](size_t size, OtherType other_value)
    需要强调的是,new用来创建对象并分配内存,它的行为是不可改变的,可以改变的是各种operator new,我们就可以通过重载operator new来实现我们的内存分配方案.
    

    相关文章

      网友评论

          本文标题:【Boolan】第五周笔记

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