1.指针:
c是一门经典的语言,在下开始学习c,最难的部分,就是指针了。指针即为指向某个元素或者实体的地址,但是指针的内涵,不仅仅限于地址。
不知道有没有人注意到,指针还有一个指向的涵义。如
int* fun(void *a)
{
int *b=(int*)a;
printf("%d\n",*b);
return b;
}
本身传进来的a指针是指向空的,意为a指针指向某个地址,但是这个地址负责解释的部分为void,所以编译器不能读出任何数据,但是经过强转为int*后,编译器就可以识别出它的数据结构为int型,那么解引用就可以朝int型解释,又如:
#include <iostream>
using namespace std;
int main()
{
int a = 98;
int *b = &a;
cout<<b<<" "<<*b<<endl;
char *c = (char *)b;
cout<<(void*)c<<" "<<*c<<endl; //由于cout的自动转换指针为字符,所以必须转换为void*才能输出指针
return 0;
}
输出结果为:
0x22fed4 98
0x22fed4 b
所以指针的指向某个东西,即为对编译器给出解释,从那个指针的内存位置开始,占有了多少内存,内存里面的数据结构如何,以及解引用后怎么对那段数据进行呈现。
2.引用:
开始看书本上说,引用即为别名,不是很明白。引用是别名,这是正确的。但是我考虑了以如下方式理解引用,可能更符合“编译器”一些。
个人认为引用实际上是一个指针,他是指向那个元素的地址,并且指针此元素的数据结构。但是引用的取值却与指针不同,我们可以直接用引用代表此元素的值,而使用指针的话需要使用*符号才能得到值。还有一点,我们不能主动修改引用的指向的地址,只可以使用
int &a = b;
这种方式
#include <iostream>
using namespace std;
int main()
{
int a = 98;
int &b = a;
cout<<"a's pointer:"<<&a<<" value:"<<a<<endl;
cout<<"b's pointer:"<<&b<<" value:"<<b<<endl;
return 0;
}
输出为:
a's pointer:0x22fed8 value:98
b's pointer:0x22fed8 value:98
b's pointer:0x22fed8 value:98
地址都一样,实际上,元素也是int a=98也是保存在内存中的一段地址,引用不过是使用另外一个元素记录a的信息而已,某种意义上与a并无不同,所以官方规定为“别名”;
3.虚函数:
前面提到了,既然指针是描述这个地址的大小以及元素的结构,那么当我们在使用类的时候,若想用基类的指针的新建一个派生类的对象时,弊端就出现了。由于指针只描述了基类的数据结构,所以这个时候若要使用指针取类的元素时,对应的是取基类的对象元素。所以这个时候虚函数诞生了。
虚函数使用了一份虚函数表来维护类里面指针指向元素的数据结构,这个数据结构位于模块的常量段中(某位大神的解释)
CONST SEGMENT
??_7CDerived@@6B@ ; CDerived::`vftable'
DD FLAT:?foobar@CDerived@@UAEXXZ
DD FLAT:?callMe@CDerived@@UAEXXZ
; Function compile flags: /Odt /RTCsu /ZI
CONST ENDS
根据这段代码(来源
http://blog.csdn.net/houdy/article/details/1496161 作者
houdy)
同时在类里面也会拥有一个指向虚函数表的指针:
#include <iostream>
using namespace std;
class a{
public:
int a;
void twice()
{
a=2*a;
}
};
class b{
public:
int a;
virtual void twice()
{
a=2*a;
}
};
int main()
{
cout<<"a's size:"<<sizeof(a)<<endl;
cout<<"b's size:"<<sizeof(b)<<endl;
return 0;
}
输出结果为:
a's size:4
b's size:8
b's size:8
证明拥有虚函数的类多占用了4个字节的指针地址,那个指针即为指向虚函数表的指针。所以拥有虚函数的的类每次读取类时都会先读取类最前面部分的指针,从不使用编译器的指针指向的数据结构。所以拥有虚函数的类也更消耗资源。