文档章节

# 极客班C++OOP(下)第五周笔记

NILYANG
 NILYANG
发布于 2016/04/15 00:44
字数 1497
阅读 27
收藏 1
C++

极客班C++OOP(下)第五周笔记


0.关于vptr和vtbl

虚指针vptr和虚表vtbl,通俗的说,两者主要用途就是,在继承关系中确定虚函数具体调用哪个函数时用的。

1 继承关系内存布局

子类一定含有父类的部分(part)

对于函数,继承的话,是继承调用权,而非函数的空间。

2 静态绑定

函数初始化的时候就知道成员的地址了,形如

//汇编代码
call xxxxxx

3 动态绑定 ,以及 this的解释

C++看到一个函数,有两个选择:

3.1.静态绑定

//汇编形式:
//__call_@ 0xABDI398A4@func1

3.2.动态绑定

如果符合某些条件:

  • a) 通过指针
  • b) 指针是向上转型的关系
  • c) 调用的是虚函数

只要符合上面的三个条件,编译器就把代码编译成

class A{
public:
    virtual void vfun1();
    void func2();
};
class B:public A{
public:
    virtual void vfun1();
    void fun2();
};

//调用

A p=new B();//B是A的派生类,并且调用了虚函数
p->vfun1();//虚函数,覆盖了父类的虚函数,动态绑定
p->fun2(); //普通函数,静态绑定,无需动态确定,毕竟B里面本来就有了

编译器看来,p->vfun1() 的形式如下:

(*(p->vptr)[n])(p);
//或者
(* p->vptr[n])(p);

即,调用虚函数之前,我们仍然不知道该函数对应的是哪个调用(到底是调用A::vfun1()呢,还是B::vfun1())。直到查找虚函数表,编译器才最终确定是哪个调用。所以这种虚函数和对象的绑定关系,就叫做动态绑定。

3.3 this指针

其中,n 就是这个虚函数在虚表中的顺序索引,需要了解的是:

编译器在编译初期就把虚函数定义的顺序记录了下来,并对做了索引关联

所以当我们是使用指针p(向上转型)查找虚函数(p->ptr[n])的时候,虚指针就确定了对应虚函数的地址,然后再调用该虚函数*(p->ptr[n])(),实参为p,即*(p->ptr[n])(p)

说了那么多,其实p就是this

3.4 多态

上面3.3小节中说到的动态绑定(即,调用虚函数的时候才能确定是哪个对象来调用),实际上就是继承关系中的多态。

  • vptr 虚指针 , 虚函数的指针
  • vtbl 虚表 , 虚函数地址表,顺序存储了对象虚函数的地址

其中,vptr是当基类定义了虚函数的时候,如果子类继承了基类,那么子类在实例化的时候,虚指针就可以指出调用的是哪个函数了。

举个例子:

有时候我们需要在容器中存放很多不同的水果,香蕉、苹果、梨,但是容器只能存放一种东西,所以只好存指针(指针才是无差别的)。那么,存储什么类型的指针呢?我们这么多种类的东西,都是水果,所以,应该放水果的指针进去,根据之前的向上转型的例子,可以看出,具体水果可以转型为基类水果类型。

std::list<Fruit*> myList;

我们定义了一个myList,该容器存放的是 Fruit*这种类型的指针。

class Fruit{
public:
    virtual print(){ std::cout << "I'am Fruit!";}
};
class Apple:public Fruit{
public:
    virtual print(){ std::cout << "I'am Apple!";}
};
class Banana:public Fruit{
public:
    virtual print(){ std::cout << "I'am Banana!";}
};
class Pear:public Fruit{
public:
    virtual print(){ std::cout << "I'am Pear!";}
};

//那我们就可以在里面放各种派生类型了
myList.push_back(new Apple());
myList.push_back(new Banana());
myList.push_back(new Pear());

Fruit pApple = new Apple();
pApple->print();//虚函数,调用派生类自己的虚函数

4. new && delete

new 和 delete 在C++中分别是创建和回收堆内存的配对操作符。既然是操作符,那么一般就可以重载。

但是,下面这段话,并不是操作符new真正的调用,此处只是一个关键字表达式,C++会将该关键字分配到具体的operator new()上。


String *ps = new String("Hello world");
delete ps;
//数组
String *p = new String[3];
delete [] p;

上面的例子,就是表达式,表达式分解之后就是operator newoperator delete两个函数调用,以及相应内存管理过程。 ``

其中,new String("Hello world"),这是表达式,可以分解为以下几个动作。

try {
    void *mem =operator new (sizeof(String));//操作符函数,可以重载
    String *ps =  static_cast<String*>(mem);//转型
    ps->String::String("Hello world");//构造
}

而,delete 操作符,扮演的是

p->~String();  //调用析构函数
operator delete(p);//释放内存

5. 重载 new和delete

既然new和delete是操作符,那么就可以重载(c++规定可以重载的范围内)。 重载操作符,分两大类

  • 全域重载
    • ::operator new , ::operator delete
    • ::operator new[], ::operator delete[]
  • 成员重载
    • A::operator new(), A::operator delete()
    • A::operator new[], A::operator delete[]

对于全域范围内重载,有一个条件是,不能在某个特定的namespace重载,必须是全域的。

同时,new操作符返回的必须是void *类型,第一个参数必须是size_t类型。

inline void * ::operator new(size_t size);

delete操作符则类似

inline void ::operator delete(void * ptr);

6. Basic_String 扩充申请量

在这个示例中,我们学习了new的主要用途,就是用于自定义的特殊的内存分配。比如 basic_string 类型的内存分配,并不是通常我们看到的new String()对象就完了,该类还做了扩展,即在基本类型占用空间的基础上,还增加了extra大小的扩展空间,以作特殊用途。

这类特性,得以给我们极大的内存管理上的便利。

另外,new 和delete在内存分配上是配对的,但是并不意味着,new之后必然会调用delete来释放空间。这一点,在ppt中,我们已经足够了解,这里就不多做说明了。

© 著作权归作者所有

NILYANG
粉丝 14
博文 100
码字总数 19038
作品 0
杭州
高级程序员
私信 提问
Machine Learning 机器学习笔记

目录 前言 第一周:Welcome 1.1 What is Machine Learning? 1.2 Linear Regression with One Variable 第二周:Linear Regression with Multiple Variables 2.1 Multivariate Linear Regress......

一缕殇流化隐半边冰霜
2018/03/27
0
0
Machine Learning 机器学习笔记目录

目录 前言 第一周:Welcome 1.1 What is Machine Learning? 1.2 Linear Regression with One Variable 第二周:Linear Regression with Multiple Variables 2.1 Multivariate Linear Regress......

一缕殇流化隐半边冰霜
2018/04/01
0
0
极客班C++ STL(容器)第二周笔记

极客班 C++ STL (容器算法)第二周笔记 标签(空格分隔): C++ 1. 容器(下) 1.1 Stack a. 概述 Stack 是一种先进先后出(First In Last Out)的数据结构,只有一个出口。特点: 支持的操...

NILYANG
2016/04/15
116
0
ApacheCN 人工智能知识树 v1.0

Special Sponsors 贡献者:飞龙 版本:v1.0 最近总是有人问我,把 ApacheCN 这些资料看完一遍要用多长时间,如果你一本书一本书看的话,的确要用很长时间。但我觉得这是非常麻烦的,因为每本...

ApacheCN_飞龙
05/18
0
0
这套GitHub 1300星的NLP课程即将完结,视频授课,在线答疑丨课程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yH0VLDe8VG8ep9VGe/article/details/84076597 郭一璞 发自 椰子树下 量子位 报道 | 公众号 QbitAI 最近NLP方向...

量子位
2018/11/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

rime设置为默认简体

转载 https://github.com/ModerRAS/ModerRAS.github.io/blob/master/_posts/2018-11-07-rime%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%BB%98%E8%AE%A4%E7%AE%80%E4%BD%93.md 写在开始 我的Arch Linux上......

zhenruyan
今天
5
0
简述TCP的流量控制与拥塞控制

1. TCP流量控制 流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。 原理是通过确认报文中窗口字段来控制发送方的发送速率,发送方的发送窗口大小不能超过接收方给出窗口大小。...

鏡花水月
今天
10
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
1K
11
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部