本篇笔记主要分为两个主要部分,第一部分关于对象模型,第二部分是关于new和delete的更加深入的学习。

一、对象模型

  1. 关于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)的更多相关文章

  1. [GeekBand] C++ 高级编程技术 (1)

    一.类型转换 class Fraction { public: explicit Fraction(int num, int den=1) : m_numerator(num), m_denomina ...

  2. C#高级编程技术复习一

    从基本的Socket编程进入 (注意:这是转的一篇2011年的文章,有些知识可能该更新了!) 这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Soc ...

  3. Qt高级编程 高清PDF+源|网盘下载地址附提取码|

    书籍作者:Mark Summerfield(马克 . 萨默菲尔德)(英)   书籍译者:闫锋欣内容简介:本书是一本阐述Qt高级编程技术的书籍.本书以工程实践为主旨,是对Qt现有的700多个类和上百万字 ...

  4. 【进阶技术】一篇文章搞掂:Spring高级编程

    本文篇幅较长,建议合理利用右上角目录进行查看(如果没有目录请刷新). 本文基于<Spring5高级编程>一书进行总结和扩展,大家也可以自行研读此书. 十一.任务调度 任务调度主要由三部分组 ...

  5. 读《C#高级编程》第1章问题

    读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...

  6. MVC高级编程+C#高级编程

    本人今年的目标是学习MVC高级编程和C#高级编程,把自己的基础打的扎实,本文中值是一个开到,定期会在上面记录学习的技术点和心得就,加油吧!!!!!

  7. jquery插件开发继承了jQuery高级编程思路

    要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台一样,得平台者得天下.苹果,微软,谷歌等巨头,都有各自的平台及生态圈 ...

  8. 转载--提高C++性能的编程技术

    读书笔记:提高C++性能的编程技术   第1章 跟踪范例 1.1 关注点 本章引入的实际问题为:定义一个简单的Trace类,将当前函数名输出到日志文件中.Trace对象会带来一定的开销,因此在默认情况 ...

  9. Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程

    Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程 教程简介: 本教程共71节,主要介绍了shell的相关知识教程,如shell编程需要的基础知识储备.shell脚本概念介 ...

随机推荐

  1. html+css 知识整理

    1.学网页最好的方法:学习别人的网页. 2.文档结构 <html>(超文本标记语言) <head>  <title>     </title>    & ...

  2. Getting NHibernate to generate a HiLo string ID

    We've got a large system that's loosely bound to its data source (Navision) via Unity - we're gettin ...

  3. 0c-35-自动释放池使用注意

    .autorelease使用注意 )并不是放到自动释放池中,都会自动加入到自动释放池 1.1) 因为没有调用autorelease方法,所以对象没有加入到自动释放池. int main(){ @aut ...

  4. windows 8.1 在硬盘上创建扩展分区

    管理员-命令提示符-> diskpart    -->进入分区工具. list disk     -->显示电脑连接磁盘数. select disk *  -->选择编号为*的 ...

  5. jquery动态生成css样式表

    $(function(){     var a=new Date().getTime();// 实时加载,目的是清除缓存    $("head").append('<link ...

  6. http协议学习(一)http状态

    整理了一个脑图,算作是<图解HTTP>的读书笔记,这本书有很多插图,适合初学者入门理解. 讲解网络协议的书不多 其中两本被人们奉为圣经  <TCP/IP详解 卷一> <H ...

  7. ApplePay高调入华,教你在app里上线ApplePay

      ApplePay在中国上线后,就有许多线上app前后脚加入了对其的接入支持,个人比较喜欢的ENJOY也抢在首批接入了ApplePay应用内支付.本文将分享作者的接入经验. ApplePay是苹果公 ...

  8. 【Shell脚本学习1】Shell简介:什么是Shell,Shell命令的两种执行方式

    Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地解释 ...

  9. Genymotion——PC上也能玩部落冲突 Clash of Clans

    零.前言 部落冲突(Clash of Clans)是手机上一个很不错的联机对战游戏,安卓和iOS平台上都有. 最近玩这个略上瘾,前几天看到部落里有人说用模拟器可以在电脑上玩,就想自己试试. 不想看我啰 ...

  10. Hadoop2.0重启脚本

    Hadoop2.0重启脚本 方便重启带ha的集群,写了这个脚本 #/bin/bash sh /opt/zookeeper-3.4.5-cdh4.4.0/bin/zkServer.sh restart ...