引出:写个类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. 关于Drupal中使用hook_schema建立数据库报错PDOException: SQLSTATE[42000]的解决办法

    报错信息如下:PDOException: SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too l ...

  2. 简单方法实现无刷新提交Form表单

    前几天遇到一个前端的问题.我希望提交表单后页面不跳转且不刷新当前页面,然而查了很多方法都没有解决. 由于Form 是提交后一定刷新页面的,所以我们可以用一个折中的办法.我们给Form 指定一个ifra ...

  3. 想转行学Java,却又担心自己半路出家成不了大牛

    想转行学Java,却又担心自己半路出家成不了大牛 很多人看好Java编程的高薪前景,在自己职业生涯迷茫的时候,想转行学Java,却又担心自己半路出家成不了大牛,赚不到钱,本文就为大家分析一下,转行学J ...

  4. A TLS packet with unexpected length was received 解决方法

    参考:A TLS packet with unexpected length was received. 系统环境 主系统 OS X,虚拟机 Ubuntu 14.04 64bit. 问题描述 在git ...

  5. TCP状态迁移

    TCP的状态变迁图 CLOSED:表示初始状态.对服务端和C客户端双方都一样. LISTEN:表示监听状态.服务端调用了listen函数,可以开始accept连接了. SYN_SENT:表示客户端已经 ...

  6. SPOJ - PGCD Primes in GCD Table(莫比乌斯反演)

    http://www.spoj.com/problems/PGCD/en/ 题意: 给出a,b区间,求该区间内满足gcd(x,y)=质数的个数. 思路: 设f(n)为 gcd(x,y)=p的个数,那么 ...

  7. LA 3942 背单词

    https://vjudge.net/problem/UVALive-3942 题意: 给出一个由S个不同单词组成的字典和一个长字符串.把这个字符串分解成若干个单词的连接,有多少种方法?比如,有4个单 ...

  8. Shell脚本之无限循环的两种方法

    for #!/bin/bash ;i<;)) do let "j=j+1" echo "-------------j is $j ----------------- ...

  9. Jmeter非GUI模式启动

    首先我们需要了解,GUI和非GUI模式启动Jmeter对测试的影响:Jmeter可视化界面及监听器展示结果需要消耗负载资源,从而导致,在大并发的情况下GUI方式会导致负载机资源紧张,对性能造成影响 e ...

  10. java反射教程

    什么是反射,为什么它是有用的,以及如何使用它? 1.什么是反射? “反射通常是JVM中运行的程序需要检测和修改运行时程序的行为的一种能力.”这个概念通常与内省(Introspection)混淆.以下是 ...