[GeekBand]C++高级编程技术(2)
本篇笔记主要分为两个主要部分,第一部分关于对象模型,第二部分是关于new和delete的更加深入的学习。
一、对象模型
关于vptr(虚指针)和vtbl(虚函数表)
只要用到了虚函数,对象中就会多一个指向虚函数表的虚指针。在32位环境下,将占4Bytes的空间。
在vtbl中,每一项都是指向自己类应当调用的虚函数的函数指针。
这里提一下,如果父类定义成虚函数,子类中和父类虚函数相同名称,参数表相同的函数会自动变成虚函数。不管加没有virtual关键字。通常我们还是要加上关键字来确保代码可读性。
2.静态绑定与动态绑定
在C中,对于不同的函数名采用静态绑定的方法,每个函数直接对应了一个地址,存储在相应的位置中。在C++中,非虚的成员函数也用静态绑定的方式被存储。如上图中的A::func1()等成员函数。
不过对于虚函数,C++中采用了动态绑定的方法。在上图中,每个虚函数都存储在虚函数表中。当调用虚函数时,编译器会随着上图中的路径找到正确的函数调用。
由于动态绑定,不管什么地方调用虚函数,总能得到正确的结果这个机制限制了虚函数应该被虚函数覆盖。
虚函数可以由以下的两种方式得到。
一个非常常用的基于虚函数的方式是建立一个指向抽象类的指针链表,这是多态的体现。
关于类的对象模型的内存分配,涉及到对象的位对齐的规则,在博客中的另一篇文章中有简单介绍。
二、再谈new和delete
定位new和delete运算符及其重载
new和delete在使用时可以有一个可选的指针类型的参数,用来指定内存分配的起始地址。如果没有这一参数,则会在堆空间中自动分配一段合适大小的空间。
默认的一个定位new函数是:
void* operator new(size_t size,void *start)
在使用时可以采用例如如下的方法:int *p = new(0x12345678) int;
事实上,我们还可以使用别的参数列进行new操作,我们也可以对operator new和operator delete进行重载。例如下面的两个常用的参数列:
void* operator new(size_t size,long extra)//extra参数用于多申请一段存储空间,专门用来存储一些特别的信息,例如引用计数的信息。
void* operator new(size_t size,long extra,char init)
new和delete操作固有的定义是:
inline void * operator new(size_t size){return malloc(size);}
inline void * operator new[](size_t size){return malloc(size);}//这里在调用时size会自动进行计算。
inline void operator delete(void * ptr){free(ptr);}
inline void operator delete[](void * ptr){free(ptr);}
注意,如果需要进行重载,new第一个参数始终应该是size_t格式的,delete的第一个参数始终是void*类型。此外,通常情况下有参数的delete不会被调用,而是继续采用无参的方法,直接释放内存。
此外,new和delete也可以作为类的成员函数重载,这样重载出来的操作将仅用于这个类。需要注意的是,new和delete操作应该是静态的,其原因在于:
1.创建对象时,先分配内存,此时没有对象,只能是静态的。
2.删除对象,先执行析构,析构后已经没有对象了,所以这里也只能是静态的。
只有当构造函数失败时,才会去寻找匹配的operator delete函数去释放空间;如果没有定义相应的delete函数,就代表放弃处理创建失败的这一异常。
注:
当一对operator new 和operator delete除了第一个参数之外,剩下的参数都一致时,称这两个操作"匹配"。
从new操作显式抛出异常,并不会触发特殊的delete。从new操作中抛出异常,代表内存分配没有进行,因此也就不需要释放内存;只有再分配内存之后,构造时产生异常才会触发特殊的delete。
下面是关于new、delete操作的测试代码:
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
#include <iostream>
#include <stdlib.h>
using namespace std;
class Fruit{
int no;
double weight;
char key;
public:
void print() { cout<<"no =\t"<<no<<"\tweight =\t"<<weight<<"\tkey =\t"<<key<<endl; }
virtual void process(){ }
Fruit(int n = ,double w = ,char k = ):no(n),weight(w),key(k){ cout<<"Ctor of Fruit is called.\nno =\t"<<no<<"\tweight =\t"<<weight<<"\tkey =\t"<<key<<endl;}
virtual ~Fruit(){cout<<"Dtor of Fruit is called.\nno =\t"<<no<<endl;}
}; class Apple: public Fruit{
int size;
char type;
public:
void save() { }
virtual void process(){ }
Apple(int s = ,char t = ,int n = ,double w = ,char k = ):Fruit(n,w,k),size(s),type(t){
cout<<"Ctor of Apple is called.\n";
print();
cout<< "size =\t"<<size<<"\ttype = \t"<<type<<endl;
//注:下面这条语句是为了测试重载版本的delete而特别添加的.
if(s<) throw ;
}
virtual ~Apple(){
cout<<"Dtor of Apple is called."<<endl;
}
static void * operator new(size_t size);
static void * operator new(size_t size,long extra);
static void operator delete(void * ptr);
static void operator delete(void* ptr,long extra);
};
void* operator new(size_t size)
{
cout<<"::operatornew(size_t) is called.\n";
return malloc(size);
}
void operator delete(void* ptr)
{
cout<<"::operatordelete(void*) is called.\n";
free(ptr);
} #endif // HEADER_H_INCLUDED
头文件
#include "header.h" using namespace std; void* Apple::operator new(size_t size)
{
cout<<"void* Apple::operator new(size_t) is called.\n";
return malloc(size);
} void* Apple::operator new(size_t size,long extra)
{
cout<<"void* Apple::operator new(size_t,long) is called.\n";
return malloc(size+extra);
} void Apple::operator delete(void* ptr)
{
cout<<"void Apple::operator delete(void*) is called.\n";
free(ptr);
}
void Apple::operator delete(void* ptr,long extra)
{
cout<<"void Apple::operator delete(void*,long) is called.\n";
free(ptr);
} int main()
{
cout<<"======Fruit new,delete test=======\n";
//这部分的结果体现了:
//1.在没有为类指定单独的new和delete时,会调用全局的new和delete;
//2.造函数先分配内存,再进行构造的特点和析构函数先进行析构,再释放内存的特点;
Fruit * _Fruit = new Fruit(,,'A');
delete _Fruit;
cout<<"======Apple new,delete test======\n";
//这部分的结果体现了:
//1.继承关系先构造基类,再构造自身
//2.析构时先析构自身,再析构基类
//3.类里边有单独的new和delete时,使用类内定义的new和delete。
Apple * _Apple = new Apple(,,,,'B');
delete _Apple;
cout<<"===Apple multi-parameters new,delete test===\n";
//这部分的结果体现了:
//1.显式delete操作总是调用delete(void*),而不管是如何new出来的
_Apple = new(long()) Apple(,,,,'C');
delete _Apple;
cout<<"=Apple new,delete failed test=\n";
//这部分的结果体现了:
//1.当ctor失败时,会去寻找匹配的dtor.
Apple * _Apple_2;
try{
_Apple_2 = new(long()) Apple(-);
}
catch(...)
{
cout<<"Catch Error.";
}
return ;
}
[GeekBand]C++高级编程技术(2)的更多相关文章
- [GeekBand] C++ 高级编程技术 (1)
一.类型转换 class Fraction { public: explicit Fraction(int num, int den=1) : m_numerator(num), m_denomina ...
- C#高级编程技术复习一
从基本的Socket编程进入 (注意:这是转的一篇2011年的文章,有些知识可能该更新了!) 这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Soc ...
- Qt高级编程 高清PDF+源|网盘下载地址附提取码|
书籍作者:Mark Summerfield(马克 . 萨默菲尔德)(英) 书籍译者:闫锋欣内容简介:本书是一本阐述Qt高级编程技术的书籍.本书以工程实践为主旨,是对Qt现有的700多个类和上百万字 ...
- 【进阶技术】一篇文章搞掂:Spring高级编程
本文篇幅较长,建议合理利用右上角目录进行查看(如果没有目录请刷新). 本文基于<Spring5高级编程>一书进行总结和扩展,大家也可以自行研读此书. 十一.任务调度 任务调度主要由三部分组 ...
- 读《C#高级编程》第1章问题
读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...
- MVC高级编程+C#高级编程
本人今年的目标是学习MVC高级编程和C#高级编程,把自己的基础打的扎实,本文中值是一个开到,定期会在上面记录学习的技术点和心得就,加油吧!!!!!
- jquery插件开发继承了jQuery高级编程思路
要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台一样,得平台者得天下.苹果,微软,谷歌等巨头,都有各自的平台及生态圈 ...
- 转载--提高C++性能的编程技术
读书笔记:提高C++性能的编程技术 第1章 跟踪范例 1.1 关注点 本章引入的实际问题为:定义一个简单的Trace类,将当前函数名输出到日志文件中.Trace对象会带来一定的开销,因此在默认情况 ...
- Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程
Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程 教程简介: 本教程共71节,主要介绍了shell的相关知识教程,如shell编程需要的基础知识储备.shell脚本概念介 ...
随机推荐
- MUI功能列表
打开App引导页面 - http://www.bcty365.com/content-146-4970-1.html 页面传值 - http://www.bcty365.com/content-1 ...
- 代码片段 - Golang 实现简单的 Web 服务器
------------------------------ 下面一段代码,实现了最简单的 Web 服务器: 编译环境: Linux Mint 18 Cinnamon 64-bit Golang 1. ...
- 网络传输速度bps与下载文件所需时间的换算
相信很多同志都非常关注自己家的计算机上网的宽带是多少.关心单位上网的宽带是多少! 但是很多同志都经常误解网络传输速度,以至于责备网络接入商(电信.网通.铁通等单位)欺骗用户,限制上网的速度! 本文,就 ...
- iOS 自定义 shareSDK 容器
- (void)initializePlat { //添加新浪微博应用 [ShareSDK connectSinaWeiboWithAppKey:@"3201194191" app ...
- 友盟分享各平台URL设置
首先,想要进项友盟分享,需要到各平台去申请ID和KEY 比如想进行微信分享,就到微信开发者平台去创建应用,拿到对应的id和appScreat,然后进行设置: 参考资料
- Float精度 在JS的解决方法
最近在做一个工资核算的系统,所有的运算全部在前台进行,因此用了的是JS来做. 做完以后,经手工核算,发现一个奇怪的问题.就是JS算出来的结果跟用计算器算出来的结果有差距. 想了很久,也没有想出问题出在 ...
- 我关于SecureCRT远程连接失败的问题解决办法
使用VirtualBox搭建一个ubuntu14.04的系统环境,为了省去主机与虚拟机直接互相直接一直切换的频繁操作,所以想到了使用SecureCRT连接,但是出现了连接问题,问题如下图:
- Umbraco(4)-Outputting the Document Type Properties(翻译文档)
翻译原文地址:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/umbraco4outputting-the-document-typ ...
- hdu 4714 树形DP
思路:dp[i][0]表示第i个节点为根的子树变成以i为一头的长链最小的花费,dp[i][0]表示表示第i个节点为根的子树变成i不是头的长链最小花费. 那么动态方程也就不难想了,就是要分几个情况处理, ...
- Differential Geometry之第二章曲线的局部理论
第二章.曲线的局部理论 2.1 曲线的概念 关于非正则曲线的讨论: ,这是个非正则点(尖点),且它是非正则曲线. 直观上,间断点,孤立点,结点(交叉点),尖点是非正则点. 有记载说:当同一条曲线用不同 ...