指针与C++基本原理
面向对象的编程和传统的过程性编程的区别在于,
OOP强调在运行阶段(而不是编译阶段)进行决策。
运行阶段指的是,程序正在运行时,编译阶段指的是编译器将程序组合起来时。
运行阶段进行决策提供了灵活性,可以根据当时的情况进行调整。
例如,为数组分配内存的情况,传统的情况是声明一个数组,而在C++中声明数组,必须提前指定数组的长度,因此,数组的长度在编译时就已经确定好了,这就是编译阶段的决策.为了安全起见,通常数组开得都比较大,但程序在大多数情况下都是浪费了内存的
OOP将这样的决策推迟到运行阶段,来动态地确定数组的长度。C++采取的方法是,使用关键字new来请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。
OOP并非C++独有,但是C++这一特性,使得代码比C语言简单。
指针的声明和初始化
指针名表示的是地址。*运算符,称为间接值(indirect value)0 )或者解除引用(dereferencing)运算符,将其应用于指针,可以得到该地址处所储存的值(C++是根据上下文来确定所指的值是乘法还是解除引用)。例如p是一个指针,则p表示的是一个地址,*p表示存储在该处的值
/**@brief 演示指针的用法
*
* lq_pointer_demo(),通过简单的示例演示了指针的用法
*/
void lq_pointer_demo()
{
using namespace std;
int target = 1;
int *pointer;
pointer = ⌖
cout << "target=" << target << endl;
cout << "*pointer=" << *pointer << endl;
cout << "Address:&target=" << &target ;
cout << ",pointer=" << pointer << endl;
cout << "Now, use pointer to change the value..." << endl;
*pointer = *pointer + 200;
cout << "Now target=" << target << endl;
cout << "*pointer=" << *pointer << endl;
cout << "Address:&target=" << &target ;
cout << ",pointer=" << pointer << endl;
}
Tip:*两边的空格是可选的,通常,C程序员使用这种格式:
int *ptr;
来强调*ptr是一个int类型的值.
而C++程序员很多采用这种格式:
int* ptr;
来强调int* 是一种类型——指向int类型的指针。
这两种格式对于机器来说没有区别,甚至不加空格,或者*两边都有空格也没有关系。
和数组一样,指针都是基于其他类型的,但指向不同类型的指针,它们本身的长度通常是相同的。地址的长度,既不能指示关于变量的长度或类型的任何信息,也不能指示该地址上有什么值。
一般来说,地址需要2或者4个字节,这取决于计算机系统(有些系统可能需要更大的地址,系统可以针对不同的类型使用不同长度的地址)。
!!!指针的危险
一定要在对指针使用*(解除引用运算符)之前,将指针初始化为一个确定的、适当的地址!!!
例子:
long *ptr;
*ptr=250;
ptr没有初始化,所以其可能是任何值。而按照程序,不管其是任何值,其都会被解释为存储250的地址。ptr指向的地方,可能并非是要存储250的地方,因此可能导致最隐秘、最难跟踪的bug.
指针和数字
指针和整型式截然不同的类型,不能简单地将整数赋值给指针;
例如:
int *ptr;
ptr=0x001AFD28;//违法,左侧为指向int型的指针,右侧为整型
可以通过强制类型转换,将数字转换为合适的地址
int *pt;
ptr=(int* )0x001AFD28;//类型匹配
但是,pt是int型的地址,但不表示pt本身就是int型。有些平台中,int 类型是2个字节,而地址则是4个字节。
指针与内存分配
指针真正的用武之地在于——在运行阶段分配未明名的内存来存储值,这种情况下,只能通过指针来访问内存。
在C中,可以使用库函数malloc( )来分配内存;C++除此之外,还有更好的方法,就是new运算符!
new运算符的作用就是为一个数据对象(可以是结构,也可以是基本数据类型)找到一个长度正确的内存快,并返回该内存块的地址。
通用使用格式格式如下:
typeName * pointer_name = new typeName;
和传统的指针初始化方法:
typeName target;
typeName* pointer_name=⌖
两者相比 ,使用new建立的指针指向的内存没有名称,它使得程序在管理内存方面有更大的控制权。
/**@brief 用来演示new动态分配内存的过程
* @param 无
* void lq_use_new_demo()
*/
void
lq_use_new_demo()
{
using
namespace
std;
int
* p1 =
new
int
;
*p1 = 1001;
double
* p2 =
new
double
;
*p2 = 1001.0;
cout
<<
"int value="
<<
*p1
<<
", location="
<<
p1
<<
endl;
cout
<<
"double value="
<<
*p2
<<
", location="
<<
p2
<<
endl;
cout
<<
"size of p1="
<<
sizeof
(p1)
<<
", size of *p1="
<<
sizeof
(*p1)
<<
endl;
cout
<<
"size of p2="
<<
sizeof
(p2)
<<
", size of *p1="
<<
sizeof
(*p2)
<<
endl;
}
例子中int 指针和double指针的长度相同,都是地址,但是由于声明了指针的类型,所以显示出(*p1)是4字节的int型,(*p2)是8字节的double型。
对于指针,new分配的内存块通常和常规变量声明分配的内存块不同,变量的值都存储在称为栈(stack)的内存区域中,而new从被称为堆(heap)或者自由存储区(free store)的内存区域分配内存。
对于内存耗尽以及分配失败的情况,C++提供了相应的处理机制和工具。
new 一定要和 delete 配对使用!!!
也就是,只能用delete来释放使用new分配的内存,但是对于空指针使用delete是安全的。
否则可能发生内存泄漏(memory leak)的情况,被分配的内存再也无法使用。如果内存发生严重泄漏,则程序将会由于不断寻找更多内存而终止。
int *ptr = new int;
...
delete ptr;
这样将会释放ptr指向的内存,但是不会删除ptr本身。需要时,还可以将ptr重新指向一个新分配的内存块。
另外:不要释放已经释放的内存块(这样做,结果将是不确定的);
不能使用delete来释放声明变量所获得的内存