引出:写个类A,声明类A指针指向NULL,调用类A的方法会有什么后果,编译通过吗,运行会通过吗?

(在VS2008与VC++的情况下) 有错误欢迎批评指正!

#include<stdio.h>
#include<iostream>
using namespace std; class base{
int a;
public:
void fun(){
printf("base fun\n");
}
}; int main(){
base *b=NULL;
b->fun();
}

看到这个的时候,一定以为运行会报错吧。

但是奇迹般的,编译器输出了:base fun

#include<stdio.h>
#include<iostream>
using namespace std; class base{
int a;
public:
virtual void fun(){
printf("base fun\n");
}
}; int main(){
base *b=NULL;
b->fun();
}

在看这个代码,还以为会输出base fun么,又错了,运行报错!

为什么会是这个结果?

#include<stdio.h>
#include<iostream>
using namespace std; class base{
int a;
public:
virtual void fun(){
printf("base fun\n");
}
void fun2(){
printf("base fun\n");
}
}; int main(){
base *b=NULL;
b->fun();
b->fun2();
}

可以发现,一个是虚函数,一个普通函数

在观察下内存中得情况:

发现果然虚函数还没在内存中,而fun2已经在内存中了

在看看汇编:

明显发现虚函数的调用比普通函数多了好几个步骤,

ecx 中放的this 指针,所以this=0(NULL),但是普通函数fun2放在全局内存区,所以可以访问

而虚函数是根据虚函数表寻找的,这时没有虚函数表,自然就没法查到虚函数的地址了

感谢 hoodlum1980 更详细的说明:因为非虚函数的地址对编译期来说“静态”的,也就是函数地址在编译期就已经确定了,实例地址对于非虚函数只是那个 this 指针参数。所以只要不访问类的实例数据就没什么问题。而虚函数的地址,是先到实例的地址前面去查找它的虚函数表所在的地址。然后从虚函数表里取出该函数所对应的元素(虚函数表是一个函数指针数组)来call的。(当然一个已知的类的虚函数表的内容也是编译期静态的,但不同类的虚函数表内容不同,即运行时多态的基础)所以实例如果为NULL,是个有特殊意义的值,是会触发运行时错误的。

总结:类中的虚函数是动态生成的,由虚函数表的指向进行访问,不为类的对象分配内存,就没有虚函数表就无法访问。

   类中的普通函数静态生成,不为类的对象分配内存也可访问。

指向NULL的类的更多相关文章

  1. c++,基类声明的指针变量和引用类型变量可以指向派 生类的对象

    基类声明的指针变量和引用类型变量可以指向派生类的对象,而反过来派生类的指针却不能指向基类变量. 这与基类和派生类之间,被允许的赋值方向是相反的. 但是从逻辑上很容易推敲其合理性.

  2. .NET面试题系列[11] - IEnumerable<T>的派生类

    “你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...

  3. Objective-C Runtime 运行时之一:类与对象

    Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一 ...

  4. Guava学习笔记(1):Optional优雅的使用null

    转自:http://www.cnblogs.com/peida/archive/2013/06/14/Guava_Optional.html 参考:[Google Guava] 1.1-使用和避免nu ...

  5. iOS RunTime运行时(1):类与对象

    Objective-C语言是一门动态语言,他将很多静态语言在编译和链接期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码更具有灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一下 ...

  6. (转)c++类的成员函数存储方式(是否属于类的对象)---一道面试题引发的思考

    昨天去面试一家公司,面试题中有一个题,自己没弄清楚,先记录如下: class D { public: void printA() { cout<<"printA"< ...

  7. [BS-22] Objective-C中nil、Nil、NULL、NSNull的区别

    Objective-C中nil.Nil.NULL.NSNull的区别 1.定义: nil:      OC语言定义:#define nil __DARWIN_NULL   /  #define __D ...

  8. null&this&super&向上转型

    向上转型:父类声明子类实例化对象 例如A是父类 B是子类    1 : 实际上是父类的对象但将会丢失子类没有的父类方法 ,如果调用方法 将会调用子类重写的父类方法    2:上转型对象不能调用子类新增 ...

  9. C Primer Plus之存储类、链接和内存管理

    存储时期即生存周期——变量在内存中保留的时间 变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量. 注意:生存期和作用域是两个不同的概念. 作用域    作用域描述了程序中可以访问一个 ...

随机推荐

  1. Apache HttpClient4使用教程

    基于HttpClient 4.5.2 执行GET请求 CloseableHttpClient httpClient = HttpClients.custom() .build(); Closeable ...

  2. bzoj1407 / P2421 [NOI2002]荒岛野人(exgcd)

    P2421 [NOI2002]荒岛野人 洞穴数不超过1e6 ---> 枚举 判断每个野人两两之间是否发生冲突:exgcd 假设有$m$个洞穴,某两人(设为1,2)在$t$时刻发生冲突 那么我们可 ...

  3. postman中如何使用OAuth

    https://learning.getpostman.com/docs/postman/sending_api_requests/authorization/ Authorization The a ...

  4. Python学习札记(二十四) 函数式编程5 返回函数

    参考:返回函数 NOTE 1.高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. eg.求和函数 #!/usr/bin/env python3 def calsums(*args): a ...

  5. 【Jmeter】配置不同业务请求比例,应对综合场景压测

    背景 在进行综合场景压测时,遇到了如何实现不同的请求所占比例不同的问题. 有人说将这些请求分别放到单独的线程组下,然后将线程组的线程数按照比例进行配置. 这种方法不是很好,因为服务器对不同的请求处理能 ...

  6. flutter 安装详细教程

    Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK.Flutter 兼容现有的代码,免费且开源,在全球开发者中广泛被使用. 安 ...

  7. Miller_Rabin(米勒拉宾)素数测试

    2018-03-12 17:22:48 米勒-拉宾素性检验是一种素数判定法则,利用随机化算法判断一个数是合数还是可能是素数.卡内基梅隆大学的计算机系教授Gary Lee Miller首先提出了基于广义 ...

  8. ORA-12505: TNS: 监听程序当前无法识别连接描述符中所给出的SID等错误解决方法

    程序连接orarle报ORA-12505错误 一.异常{ ORA-12505, TNS:listener does not currently know of SID given in connect ...

  9. numpy.zeros(np.zeros)使用方法--python学习笔记31

    https://blog.csdn.net/qq_26948675/article/details/54318917

  10. C#MVC中@HTML中的方法

    //生成表单 @{ Html.BeginForm("Index", "Simple", FormMethod.Post, new { id = "my ...