资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

C++面向对象编程(下)-创新互联

文章目录
    • C++面向对象编程(下)
      • 11. vtpr(virtual pointer)和vtbl(virtual table)
        • 1. 前提须知
        • 2. 静态绑定和动态绑定
        • 3. vtpr和vtbl
        • 4. 多态
        • 5. 图示一
        • 6. 图示二
        • 7. template method补充
      • 12. const member functions(常量成员函数)
        • 1. 形式(const加的位置):
        • 2. member functions 与 object 的规则
        • 3. const member functions 和 non-const member functions 的特殊规则
      • 13. 重载operator new、delete、 new[]、delete[]
        • 1. 知识回顾
        • 2. 重载 ::operator new、new[] 和 ::operator delete、delete[]
        • 3. 重载 member operator new 和 member operator delete
        • 4. 重载 member operator new[] 和 member operator delete[]
        • 5. operator new、delete、 new[]、delete[] 使用示范
          • 1. member operator new、delete、 new[]、delete[]重载写法
          • 2. member operator new、delete、 new[]、delete[]调用过程
          • 3. 显式调用global operator new、delete、 new[]、delete[]过程
      • 14. 重载member operator new(...) 、 delete(...)
        • 1. new(...) 、delete(...)和new、delete的区别
        • 2. member operator new(...)
        • 3. member operator delete(...)
        • 4. member operator new(...)、delete(...)重载书写和使用
        • 5. 标准库string重载placement new

网站建设公司,为您提供网站建设,网站制作,网页设计及定制网站建设服务,专注于成都定制网页设计,高端网页制作,对户外休闲椅等多个行业拥有丰富的网站建设经验的网站建设公司。专业网站设计,网站优化推广哪家好,专业seo优化排名优化,H5建站,响应式网站。C++面向对象编程(下) 11. vtpr(virtual pointer)和vtbl(virtual table) 1. 前提须知
  • 实例对象在内存的表现:
    • 函数在内存中的位置,与实例对象占用的内存没有关系;
    • 对于一个类的多个对象,函数在内存中也只会有一份
    • 实例对象占用的内存,只会包含对象的数据;如果有虚函数,还会包含一个虚指针;【无论有多少个虚函数,都只会多占用一个指针的空间;】
  • 继承:
    • 对于父类中的函数,子类只是继承了函数的调用权,不会为子类在内存中另外生成一份函数【因为函数始终只在内存占一份】;
    • 如果父类有虚函数,那么子类也一定有虚函数(继承);
    • 虽然父类和子类中的非虚函数和数据,命名可以重复(因为二者没有什么关系),但这样的命名风格不好。
2. 静态绑定和动态绑定
  • 类型:
    • 静态类型:对象在声明时采用的类型,在编译期既已确定;
    • 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
  • 绑定(Binding):是指将变量和函数名转换为地址的过程。
    • 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
      • 意味着绑定的函数或者变量,已经在编译阶段,该语句已经被编译成“call 函数地址”或"callq 函数地址"这样的汇编指令格式,并且这些汇编指令中的函数地址在程序编译后是固定不变的,请记住,所有函数都有唯一的地址。
    • 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
  • 只有虚函数才使用的是动态绑定(实现多态),其他的全部是静态绑定;
  • 编译器使用动态绑定的三个条件:
    • 通过指针 / 引用调用函数;
    • 指针是向上转型后的指针(this指的是子类对象,符合向上转型);【即父类指针指向子类对象】
    • 指针调用的是虚函数。
    • 简单一句话使用基类的引用(指针)调用虚函数

3. vtpr和vtbl
  • vtpr(虚指针):

    • 对于有virtual函数的类,它的实例化对象在内存空间中除了包含数据的大小,还会多出一个指针(4个字节)的大小,同时会放在内存中最开始的位置,这个指针就是vptr(虚指针),它指向了vtbl(虚函数表),即 vptr内容 vtbl的地址。
  • vtbl(虚函数表):

    • vtbl 内容:保存了一个类持有的虚函数的地址;
    • 建两个A对象,发现他们的虚函数指针相同,这说明他们的虚函数表属于类,不属于对象。所以虚函数表应该存在共有区;
    • 位置:虚函数表放在了全局数据段。
  • 查找过程:

    • 在编译时,编译器会为【整个继承关系中的每个虚函数】进行顺序编号,按照这个编号顺序,在vtbl中放置虚函数地址;
    • 调用一个实例化对象的虚函数过程:
      • 先找到vtptr(在实例化对象所在内存的开头位置);
      • 通过vtptr内容,找到vtbl;
      • 从vtbl找到对应虚函数的地址,把它当成函数指针去调用
      • 函数指针调用的方式:后面的§,是传递隐藏参数this pointer;
        在这里插入图片描述
4. 多态
  • 多态和动态绑定是一回事(涉及虚函数、虚函数指针、虚函数表)

5. 图示一
  • 图中有A、B、C三个对象:
    • 对象关系:
      • B是A的子类,C是B的子类;
    • 对象内容:
      • A对象:2个虚函数,2个非虚函数,2个int数据;
      • B对象:
        • 自身的内容:1个非虚函数,1个int;
        • 继承的内容:继承了A的2个虚函数,但自己重新定义了其中一个;继承了A的2个非虚函数;继承了A的两个int数据;
        • 总内容:2个虚函数(其中一个自定义),3个非虚函数,3个int数据;
      • C对象:
        • 自身的内容:1个非虚函数,2个int;
        • 继承的内容:继承了B的2个虚函数,但自己重新定义了其中一个;继承了B的3个非虚函数;继承了B的3个int数据;
        • 总内容::2个虚函数(其中一个自定义),4个非虚函数,5个int数据;
    • 三个类的函数个数:
      • 4个非虚函数;
      • 4个虚函数;
        在这里插入图片描述
6. 图示二
  • A为shape抽象类;
  • B、C为具体的形状类;
  • A有draw虚函数,B、C自定义了draw函数(相当于上一页的vfunc1());
  • 右下角的容器:
    • 使用父类指针指向子类的方式(多态),使容器可以包含多种类型;
      在这里插入图片描述

7. template method补充
  • 也可以使用基类指针指向派生类对象 CDocument* cdoc = new CMyDoc(),来调用OnFileOpen()函数;
  • 执行Serialize()函数,满足动态绑定三个条件,编译器会把Serialize()写出函数指针调用的形式(图左边);
  • 在动态绑定三个条件中的第二个,不太好理解:
    • 指针是向上转型后的指针(this指的是子类对象,符合向上转型);
    • 【简单理解为只要this指的是子类对象,就满足这个条件。】
    • 可能右下角调用OnFileOpen()中的this指针类型是父类指针类型。(这样就有父类指针指向子类对象)
      在这里插入图片描述

12. const member functions(常量成员函数) 1. 形式(const加的位置):
class Complex{
public:
    // const member function(常量成员函数)
	double real () const { return resl; }
	double imag () const { return im; }
private:
	double re, im;
};
  • 常量成员函数顾名思义:
    • 只能定义在成员函数身上,全局函数不行;
2. member functions 与 object 的规则
  • const object (要求 data members 不能改动);
  • non-const object (data members 可改动);
  • const member functions(保证这个成员函数不更改 data members);
  • non-const member functions(不保证这个成员函数不更改 data members);
    在这里插入图片描述
3. const member functions 和 non-const member functions 的特殊规则
  • 一个 member function 如果同时存在 const 和 non-const 版本:

    • 明确了调用函数的对象是 const object,还是 non-const object;
    • 主要是明确了调用 const member functions 的对象(因为一般情况下,non-const object也可以调用const member functions,上面表的第一行)
      在这里插入图片描述
  • 标准库(string)的成员函数示范:

    • operator[] 函数同时存在 const 和 non-const 版本;
    • 常量成员函数的const是函数签名的一部分,所以可以进行函数重载;
    • 根据特殊规则:
      • 调用 const member functions 的对象,一定是const object;
      • 调用 non-const member functions 的对象,一定是non-const object;
        在这里插入图片描述

13. 重载operator new、delete、 new[]、delete[] 1. 知识回顾
  • new过程(分三步):

    • 先使用operator new分配空间,底层使用malloc函数;
    • 进行指针类型转换;
    • 再调用构造函数:对于带有指针的类,通过构造函数,分配指针指向的数据;
  • delete过程(分两步):

    • 先调用析构函数:对于带有指针的类,通过析构函数,释放指针指向的数据;
    • 再使用operator delete释放分配的空间,底层使用free函数。
  • 在new和delete的过程中,new和delete本身是不能重载的,但new和delete中operator new和operator delete操作是可以重载的:

    • operator new和operator delete的调用顺序
      • 先找类中的重载版本;
      • 再找全局版本。
2. 重载 ::operator new、new[] 和 ::operator delete、delete[]
  • 一般不重载全局operator new、delete、new[]、delete[];
    在这里插入图片描述
3. 重载 member operator new 和 member operator delete
  • 图中的member operator delete中的optional参数表示该参数可选;
  • 该类进行new和delete中,优先找member operator new 和 member operator delete
    在这里插入图片描述
4. 重载 member operator new[] 和 member operator delete[]
  • 图中的optional指该参数可选;
  • 该类进行new[] 和delete[]中,优先找member operator new[] 和 member operator delete[];
    在这里插入图片描述
5. operator new、delete、 new[]、delete[] 使用示范 1. member operator new、delete、 new[]、delete[]重载写法
  • (仅仅增加打印信息)

  • 重载函数:

    • operator new;
    • operator delete;
    • operator new[];
    • operator delete[];
  • global 与member operator new、delete、 new[]、delete[]相关函数的调用顺序

    • // 优先调用member version,如果没有member version,就调用global version;
      Foo* pf = new Foo;
      delete pf;
    • // 强制调用global version;
      Foo* pf = ::new Foo;
      ::delete pf;

在这里插入图片描述

2. member operator new、delete、 new[]、delete[]调用过程
  • 不带有虚函数的类:

    • 由上一页可知,sizeof(Foo) = 12【int:4个字节,long:4个字节,string:4个字节(指针)】;

    • 调用 operator new 和 delete:

      • // 调用 operator new 和 delete
        Foo* p = new Foo(7);	// 这个参数是构造函数的参数
        delete p;
      • 分配一个对象的大小空间(这个例子 == 12);

      • 注意点:

        • operator new的第一个参数是编译器自动传递的;
        • 下图左边①中后面的Foo(7)是Foo的构造函数,不要和new的参数混淆了;

    • 调用 operator new[] 和 delete[]:

      • // 调用 operator new[] 和 delete[]
        Foo* pArray = new Foo[5];
        delete pArray[];
      • 分配5个对象的大小空间 == 12*5 + 4 == 64;【调用operator new[] 时,会有一个额外整型用于记录数组的大小,整型4个字节,面向对象part one中有讲过】

    • new[] 和 delete[] 调用构造函数和析构函数的顺序相反【看this指针顺序】


  • 带有虚函数的类:

    • 唯一不同在于,带有虚函数的类,会多一个虚函数指针的大小
    • sizeof(Foo) = 16
    • 分配5个的大小 == 16*5 + 4 = 84;
      在这里插入图片描述
3. 显式调用global operator new、delete、 new[]、delete[]过程
  • 强迫使用global version,没有member version的打印信息;
    在这里插入图片描述
14. 重载member operator new(…) 、 delete(…) 1. new(…) 、delete(…)和new、delete的区别
  • 括号的意义:operator new() 、delete() 额外设置了参数;
  • placement术语介绍:
    • member operator new() == placement new;
    • member operator delete() == placement delete;
2. member operator new(…)
  • member operator new() 可以有多个参数,但第一个参数类型一定是size_t(unsigned int),并且这个参数是由编译器传递;

  • 不同版本的 member operator new() 必须有不同的参数列(才能进行函数重载,函数签名不同):

    • 即第一个参数之后的参数,个数、类型要有区别;
  • member operator new() 调用方式:

    • 调用时,需要手动传递的参数是第一个参数以后的参数;

    • 下面的 member operator new() 说明,定义时有三个参数;

    • // 调用 operator new() 和 delete
      // new(300, 'c'):说明这个operator new()有三个参数,第一个参数不需要手动传递。
      Foo* p = new(300, 'c') Foo(7);	// 注意传递new的参数,以及构造参数的位置
      delete p;
3. member operator delete(…)
  • member operator delete() 也可以重载,也可以不重载;
  • 这一步不是必须的,因为不会被delete调用【正常 delete 调用 void operator delete(void*, size_t) 版本】;
  • member operator delete() 调用:
    • 无论new调用的是哪种版本,只有一般版本的delete会被调用;
  • member operator delete() 作用:
    • new操作分为三步:
      • operator new;
      • 转型;
      • 构造函数;
    • 只有当 new 中第三步,调用的构造函数抛出异常,才会调用这些 member operator new() 版本对应的 operator delete() ,释放之前 member operator new() 分配的空间;
    • 它只可能这样被调用,主要用来归还未能完全创建成功的对象所占用的内存空间。
      在这里插入图片描述
4. member operator new(…)、delete(…)重载书写和使用
  • member operator new():

    • (1) ~ (4) 不同版本的 placement new;
    • (5):故意写错第一参数的 placement new 不合法版本;编译器会报错,要求第一个参数类型是 size_t;
      在这里插入图片描述
  • member operator delete():

    • (1) ~ (4) 的 placement delete 对应上一页不同版本的 placement new;
    • 【上一页在构造函数中故意抛出异常;】
    • 右边⑤:构造函数失败(抛出异常),是否会调用对应的 placement delete,取决于编译器;
    • 最下面的方框:
      • 没有定义对应 operator new(…) 版本的 operator delete(…),也不会报错;
      • 这意味着放弃构造函数抛出的异常。
        在这里插入图片描述
5. 标准库string重载placement new
  • 重载 placement new 用于分配额外的空间,作为 string 的内容空间;
  • Rep:是标准库 string 用于计数有多少用户共用这个字符串对象的计数器(reference counting 技术);
  • reference counting 技术:
    • 允许多个拥有共同值的对象共享同一个对象实体,解决了同一个对象存在多分拷贝的问题。
      在这里插入图片描述

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享文章:C++面向对象编程(下)-创新互联
文章位置:http://cdkjz.cn/article/ejjdc.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220