本篇笔记主要分为两个主要部分,第一部分关于对象模型,第二部分是关于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. 使用 Team Foundation 版本控制命令

    使用 Team Foundation 版本控制命令 Visual Studio 2013   其他版本 Visual Studio 2010 Visual Studio 2008 Visual Stu ...

  2. 高级进程间通信之UNIX域套接字

    UNIX域套接字用于在同一台机器上运行的进程之间的通信.虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高.UNIX域套接字仅仅复制数据:它们并不执行协议处理,不需要添加或删除网络报头,无 ...

  3. Youth(青春)

    Youth is not a time of life; it is a state of mind; it is not a matter of rosy cheeks, red lips and ...

  4. javaEE学习笔记-单例模式

    定义: 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的三要素: (1)私有的静态的成员变量 (2)私有的构造方法 (3)公共的静态的入口点方法 单例模式的分类: (1)饿 ...

  5. DataContractJsonSerializer和JavaScriptSerializer内部实现差异

    定义一个下面这样的类,此类有Serializable属性,并且有一个属性的定义没有使用自动属性来实现. [Serializable] public class Users { public int U ...

  6. 构建项目AppFuse+QuickStart

    AppFuse是一个完整的框架来构建web应用程序.它最初是为了构建新的web应用程序少走弯路.多年来,它已成为一个非常成熟的可测试和创建基于java的web应用安全系统.在其核心,AppFuse是项 ...

  7. web前端开发(1)

    web标准 结构标准.样式标准.行为标准分离 结构标准:xml xhtml  html 样式标准:css 行为标准:DOM  ECMAScript 符合标准的网页:1.标签小写  2.属性加引号 3. ...

  8. js自动刷新页面代码

    <script language="JavaScript">function myrefresh(){window.location.reload();}setTime ...

  9. vs克隆新建团队项目

    团队资源——克隆,完成后打开: 在解决方案资源中,添加——新建项目——web——ASP.NET web应用程序——空的,并将其设为启动项目:

  10. hdu1331 按着题目的公式直接写

    #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #d ...