在这里总结自己关于编程过程中的一些想法、深究和思考。
一、内敛函数和宏定义
宏定义源自C语言,可以作为不断重复常量、简单表达式的替代解决方案;而内联函数是在C++中使用的较为普遍,其作用和宏函数相差不多。但在C++中,内联函数需要做类型检查,所以较于宏函数更加安全,另外,内联函数是由嵌入代码实现而非中断调用实现,所以使用内联函数的情况最好遵循两个原则:一、一个函数被不断的重复调用;二、函数内部比较简单,没有诸如(for,if,switch等语句)。对于宏定义是建议就是最好不要使用,因为安全性、可靠性的考虑。
最后还有一点:
宏定义不是函数,它是预编译阶段将相关字符串替换成宏体;而内联函数是函数,但在编译中不单独产生,而是将有关代码嵌入到调用处。
二、指针和引用
(1)函数中的临时指针变量造成‘内存泄漏’
最常见的一种情况如下:
void GetMemory(char *p, int num)//获取内存空间
{
p = (char*)malloc(sizeof(char)*num);
}
void swap(int *p, int *q)//交换两个数
{
int *temp;
*temp=*p;
*p=*q;
*q=*temp;
}
这两个函数所产生的错误是一样的:都是内存泄漏。首先需要明确一点,任何向函数传递的参数,在函数体的内部其实都是所传值的一个副本。那么GetMemory中实质上是对副本指针分配了一块内存空间,可是函数执行完临时的指针被释放掉,但所申请的空间还在那里,可缺少了必要的指向就变成了一段死空间而产生内存泄漏,这在vs下会产生运行时错误。swap中temp指针所产生的错误也是一样的。
关于获取内存空间的方法可以采用函数返回指针值,和传递指向指针的指针的方法来完成。后者代码如下:
void GetMemory(char **p,int num)
{
*p = (char*)malloc(sizeof(char)*num);
}
三、当指针碰上数组
有这样一个观点需要牢记:数组名本身就是指针,再加上&就变成了双指针,双指针就是指二维数组,加1就是加1行。举个例子如下:
int a[] = {1,2,3,4,5};
int *ptr = (int *)(&a + 1);
printf("%d",*(ptr-1));
输出为5,因为*ptr指向a的第六个元素(尽管其不存在)。
另外,数组和指针容易让人混肴的两个概念就是指针数组和数组指针,其实从字面上就很好区分,方法就是将重音放在后尾,指针数组是数组其元素为指针,数组指针是指针其指向为数组。
四、指针和Windows句柄
指针好理解就是某个物理内存地址的标记。而句柄所代表的含义自从学Windows编程以来就一直比较含糊,总觉得是个像地址又不是地址的东西,所以在这里我就深入浅出、言简意赅的讲一下句柄到底是什么:
首先说一下它的本质,Windows句柄就是一个指向指针的指针,稍微具体一点,句柄是一个32位的整数,Windows在内存中维护的内存物理地址的整数索引。然后要说一下他为什么是这样子,就要了解一下Windows操作系统的一些基本知识,Windows是一个以虚拟内存为基础的操作系统,其内存管理器经常在内存中来回移动对象,以满足各种应用程序对内存的需要,内存被移动就意味着其实际的物理地址发生了变化。为了方便应用程序找到物理地址,操作系统为各应用程序腾出了一些实际的物理内存地址,专门用来存放对象在内存中地址的变化,而操作系统专门腾出的这个地址是不会发生变化的。所以操作系统在改变应用的内存地址后会将改变的地址存到这个专门的内存块中(即叫做句柄),应用程序只需要读这个句柄就可以了。很好的屏蔽了应用程序对于内存地址的过多关注,这样方便而且稳定。
五、智能指针auto_ptr
智能指针存在的目的是为了程序的的健壮性,试想有如下程序:
void fun()
{
T *pt(new T);
/*更多的代码*/
delete pt;
}
这个代码初看,开辟一块内存并赋以指针,函数执行完释放空间。看似正确,但是如果在/*更多的代码*/部分发生一些问题,比如说:提前返回,异常抛出等问题使得delete语句执行不到,那就产生了内存泄漏的经典问题。
所以为了避免上述问题的产生,我们可以使用智能指针,实现原理很简单:使用一个智能指针类模板,在构造时获取资源,在析构时释放资源并进行相关的指针操作。最常用的智能指针是标准C++库<utility>中的auto_ptr,其也只是智能指针中的一种。其使用格式如下:
std::auto_ptr<classA> pa(new classA)//std没有加入头文件时需要引用名字空间