C++中关于指针的理解(一点一点完善中)
2016-02-21 21:58
260 查看
一、指针
1. 指针的值是一个地址,通过间接寻址运算符*来区分地址与指针所指地址保存的值区分开。
一个变量的地址称为该变量的指针。
如果有一个变量是用来专门存放另一变量地址(即指针)的,则称它为指针变量。
要区分指针和指针变量!!!
*实际上是运算符重载(C++中提及),表示指向。
例如: int *p,i;
p = &i;
p的值是i的地址,*p的值为i的值。
指针的两个属性:内容和位置。
其中位置可以存储在另一个变量中,这样便成为了指向指针的指针。
a、指针变量的定义
基类型 *指针变量名;
可以这么说:指针变量名是指向基类型的指针变量。
所定义的指针变量是指向基类型的指针变量,或者说是指针变量只能存放基类型数据的地址。
要注意:不能用一个整数给一个指针变量赋初值。
b、引用指针变量
与指针变量有关的运算符:
&——取地址运算符
*——指针运算符,也称间接访问运算符
c、指针作为函数参数
最经典的例子是swap函数:
在上例中,如果写成这样,就不对
因为实参与形参值的传递是单项的值传递方式,只能从实参传向形参,形参的改变无法传给实参。
即调用函数时不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
为了动态的分配和回收内存空间,需要用到new和delete函数。
new:从内存中获得存储对象所需要的内存空间。
例如:p = new int;
只是程序请求足够空间来存储一个整数,这部分内存的地址存储在p中,可以简洁的通过指针对p指向的内存赋值,也可以使用赋值语句q = p将存储在p中的地址付给另一个指针。
delete p;
与delete有关的两个问题:
(1)、悬挂引用问题
执行上述语句后所释放的内存空间的地址仍然在p中,但对程序而言,这个内存空间已经不存在了,这就是悬挂引用问题。
为了避免悬挂引用问题,必须将一个特定地址赋给指针,可以将空地址赋给指针。
eg : p = 0;
意思是p变为空或者p是空的。
(2)、内存泄露问题
eg: p = new int;
p = new int;
在上面两条语句执行后,第一个p所指单元变得不可访问,导致了内存泄露问题。
应该是:
p = new int;
delete p;
p = new int;
二、指针与数组
在C++中,数组必须提前声明,但是数组大小是很难预测的,这个问题可以通过指针来解决。
例如:
数组也可以动态声明:
p = new int
;
分配了足够的空间来存储n个整数。
指针p也可以看成一个数组型变量,这样就可以使用数组符号。
例如:
如果p指向的数组现在不需要了,就必须使用一下语句将其释放:
delete [] p;
上面[]代表p指向一个数组,此外delete只用于使用new赋值的指针。
注意:
字符串是十分重要的数组类型,与指针有紧密的联系。
要注意许多预定义的函数操作字符串都是假定字符串一空字符’\0’结尾的,在使用时要注意这个问题。
三、指针与复制构造函数
1. 这个部分我想说的主要是在C++中进行对象的复制时,如果没有写复制构造函数的情况。
首先,如果类中包含指针变量,且要对对象进行复制。
这就是Node定义中没有提供复制构造函数Node ( const Node& )的结果。
当执行node2(node1)时,复制构造函数是必不可少的。
如果没有定义复制构造函数,编译器会自动生成一个,但是编译器生成的复制构造函数只是逐个对成员进行复制。
因为Node中的数据成员name是一个指针,所以编译器自动生成的复制构造函数将node1.name的字符串地址复制给node2,而不是复制字符串的内容,即node1.name和node2.name指向同一个字符串,在本例中为roger,在后面进行name的变化时,roger变为wendy,同时node1.name和node2.name同时变化。
造成了错误。
为了防止错误的产生,必须定义一个合适的复制构造函数。
使用上述复制构造函数后,声明node2(node1)生成了另一个roger副本,即node2.name与node1.name不指向同一个字符串,而是内容相同的两个字符串,这样改变node1/2的任意name对另一个不会造成影响。
2.赋值运算符也会引起同样的问题
如果用户没有提供赋值运算符的定义,下面的赋值操作
node1 = node2;
就会逐个对成员进行复制,引起上面的问题,解决方法是重载赋值运算符:
四、指针与析构函数
这部分我想讲的是含有指针数据成员的Node类型的局部对象会发生什么。
局部对象在定义它们的区域之外是无效的,会被销毁,所占用内存也将被释放。
但如果对象数据成员中含有指向字符串的指针,即指针数据成员占用的内存将被释放,而字符串占用的内存却没有释放。
在对象销毁之后,以前可以通过指针访问的字符串不能访问了,(如果没有把字符串赋给其他变量的话),则字符串所占的内存再也无法释放,从而导致内存泄露。
只要是对象具有指向动态分配空间的数据成员,都存在这个问题。
为了解决这个问题,类定义中应该包含析构函数的定义。
当销毁对象,程序从定义对象的块中退出或调用delete时,析构函数抖动调用。
析构函数不带参数,也没有返回值,所以每个类中只能有一个析构函数。
对于上面的类Node。可以定一下面的析构函数:
五、指针与引用变量
六、函数指针
其中double (*f) (double);
意味着f是一个指向函数的指针,该函数带有一个double参数,返回一个double值。
但是必须带()
例如: double *f(double);
指的是声明的函数返回一个指向double的指针。
PS:未完待续
1. 指针的值是一个地址,通过间接寻址运算符*来区分地址与指针所指地址保存的值区分开。
一个变量的地址称为该变量的指针。
如果有一个变量是用来专门存放另一变量地址(即指针)的,则称它为指针变量。
要区分指针和指针变量!!!
*实际上是运算符重载(C++中提及),表示指向。
例如: int *p,i;
p = &i;
p的值是i的地址,*p的值为i的值。
指针的两个属性:内容和位置。
其中位置可以存储在另一个变量中,这样便成为了指向指针的指针。
a、指针变量的定义
基类型 *指针变量名;
可以这么说:指针变量名是指向基类型的指针变量。
所定义的指针变量是指向基类型的指针变量,或者说是指针变量只能存放基类型数据的地址。
要注意:不能用一个整数给一个指针变量赋初值。
b、引用指针变量
与指针变量有关的运算符:
&——取地址运算符
*——指针运算符,也称间接访问运算符
c、指针作为函数参数
最经典的例子是swap函数:
#include <iostream> using namespace std; int main() { void swap( int *p1,int *p2 ); int *po1,*po2,a,b; cin >> a>>b; po1 = &a; po2 = &b; if ( a < b ) swap( po1,po2 ); cout<<"max="<<a<<"min="<<b<<endl; return 0; } void swap( int *p1,int *p2 ) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; }
在上例中,如果写成这样,就不对
void swap( int x,int y ) { int temp; temp = x; x = y; y = temp; }
因为实参与形参值的传递是单项的值传递方式,只能从实参传向形参,形参的改变无法传给实参。
即调用函数时不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
为了动态的分配和回收内存空间,需要用到new和delete函数。
new:从内存中获得存储对象所需要的内存空间。
例如:p = new int;
只是程序请求足够空间来存储一个整数,这部分内存的地址存储在p中,可以简洁的通过指针对p指向的内存赋值,也可以使用赋值语句q = p将存储在p中的地址付给另一个指针。
delete p;
与delete有关的两个问题:
(1)、悬挂引用问题
执行上述语句后所释放的内存空间的地址仍然在p中,但对程序而言,这个内存空间已经不存在了,这就是悬挂引用问题。
为了避免悬挂引用问题,必须将一个特定地址赋给指针,可以将空地址赋给指针。
eg : p = 0;
意思是p变为空或者p是空的。
(2)、内存泄露问题
eg: p = new int;
p = new int;
在上面两条语句执行后,第一个p所指单元变得不可访问,导致了内存泄露问题。
应该是:
p = new int;
delete p;
p = new int;
二、指针与数组
在C++中,数组必须提前声明,但是数组大小是很难预测的,这个问题可以通过指针来解决。
例如:
int a[5],*p; for ( sum = a[0],i = 1;i < 5; i++ ) sum +=a[i]; //就等同于 for ( sum = *a,i = 1;i < 5; i++ ) sum +=*(a+i);//表达式a+i就等于内存地址 a + i*sizeof(int) //就等同于 for ( sum = *a,p =a + 1;p <a + 5; p++ ) sum +=*p;
数组也可以动态声明:
p = new int
;
分配了足够的空间来存储n个整数。
指针p也可以看成一个数组型变量,这样就可以使用数组符号。
例如:
for ( sum = p[0],i = 1;i < n;i++ ) sum+=p[i]; //= for ( sum = *p,i = 1;i < n;i++ ) sum+=*(p+i); //= for ( sum =*p,q = p+1;q < p + n; q++ ) sum+=*q;
如果p指向的数组现在不需要了,就必须使用一下语句将其释放:
delete [] p;
上面[]代表p指向一个数组,此外delete只用于使用new赋值的指针。
注意:
字符串是十分重要的数组类型,与指针有紧密的联系。
要注意许多预定义的函数操作字符串都是假定字符串一空字符’\0’结尾的,在使用时要注意这个问题。
三、指针与复制构造函数
1. 这个部分我想说的主要是在C++中进行对象的复制时,如果没有写复制构造函数的情况。
首先,如果类中包含指针变量,且要对对象进行复制。
class Node { char *name; int age; Node(char *n="",int a = 0) { name = strdup(n); age = a; } }; Node node1(“roger”,20),node2(node1); //上面的node2(node1); 等同于 node2 = node1; strcpy( node2.name,"wendy" ); node2.age = 30; cout<<node1.name<<""<<node1.age<<""<<node2.name<<""<<node2.age; //结果是 wendy 30 wendy 20
这就是Node定义中没有提供复制构造函数Node ( const Node& )的结果。
当执行node2(node1)时,复制构造函数是必不可少的。
如果没有定义复制构造函数,编译器会自动生成一个,但是编译器生成的复制构造函数只是逐个对成员进行复制。
因为Node中的数据成员name是一个指针,所以编译器自动生成的复制构造函数将node1.name的字符串地址复制给node2,而不是复制字符串的内容,即node1.name和node2.name指向同一个字符串,在本例中为roger,在后面进行name的变化时,roger变为wendy,同时node1.name和node2.name同时变化。
造成了错误。
为了防止错误的产生,必须定义一个合适的复制构造函数。
class Node{ char *name; int age; Node ( char *n = 0,int a = 0 ) { name = strdup(n); age = a; } Node ( const Node& n )//复制构造函数 { name = strdup( n.name ); age = n.age; } };
使用上述复制构造函数后,声明node2(node1)生成了另一个roger副本,即node2.name与node1.name不指向同一个字符串,而是内容相同的两个字符串,这样改变node1/2的任意name对另一个不会造成影响。
2.赋值运算符也会引起同样的问题
如果用户没有提供赋值运算符的定义,下面的赋值操作
node1 = node2;
就会逐个对成员进行复制,引起上面的问题,解决方法是重载赋值运算符:
Node& operator= (const Node& n) { if ( this != &n ) { //no assignment to itself if ( name != 0 ) freee(name); name = strdup( n.name ); age = n.age; } return *this; } //this指针指向对象本身
四、指针与析构函数
这部分我想讲的是含有指针数据成员的Node类型的局部对象会发生什么。
局部对象在定义它们的区域之外是无效的,会被销毁,所占用内存也将被释放。
但如果对象数据成员中含有指向字符串的指针,即指针数据成员占用的内存将被释放,而字符串占用的内存却没有释放。
在对象销毁之后,以前可以通过指针访问的字符串不能访问了,(如果没有把字符串赋给其他变量的话),则字符串所占的内存再也无法释放,从而导致内存泄露。
只要是对象具有指向动态分配空间的数据成员,都存在这个问题。
为了解决这个问题,类定义中应该包含析构函数的定义。
当销毁对象,程序从定义对象的块中退出或调用delete时,析构函数抖动调用。
析构函数不带参数,也没有返回值,所以每个类中只能有一个析构函数。
对于上面的类Node。可以定一下面的析构函数:
~Node (){ if ( name != 0 ) free( name ); }
五、指针与引用变量
六、函数指针
double f( double x ) { return 2*x; } double sum ( double (*f) (double),int n ,int m ) { double result = 0; for ( int i =n; i <= m; i++ ) result += f(i); return result; }
其中double (*f) (double);
意味着f是一个指向函数的指针,该函数带有一个double参数,返回一个double值。
但是必须带()
例如: double *f(double);
指的是声明的函数返回一个指向double的指针。
PS:未完待续
相关文章推荐
- 希尔排序(C++版)
- 简单选择排序算法(C++版)
- 简单插入排序(C++版)
- 适配器模式
- 《C++Primer》 学习——第三章
- C++、C#写的WebService相互调用
- c++primer阅读笔记之数组与指针
- c++ 写注册表 并将exe添加到开机启动
- C++Primer charpter1.
- Dev-C++ 小问题锦集
- Klib - C语言通用库
- iOS 学习第十八天 OC语言 NSArray的使用
- ios 学习第十七天 OC语言 Protocol基本使用
- iOS 学习第十六天 OC语言 autorelease基本使用
- C/C++开源项目清单
- IOS学习第十五天 OC语言 内存管理简介
- iOS 学习第十四天 OC语言 OC中的私有方法
- C++中搜索、截取字符串
- c++ 读注册表信息 读取CPU信息
- C++面向对象编程<一>:简介