cpp(4)指针

//查看地址
#include<iostream>
using namespace std;
int main()
{
int i=1;
cout<<&i<<endl;
return 0;
}
----------------------------------------
空指针:


我们知道指针就是用来保存内存地址的变量,因此
我们定义一个指针后一定要用它来保存一个内存
地址,假如我们不那么做,那么该指针就是一个
失控指针,它可以指向任何地址,并且对该地址
的数值进行修改或者删除,后果是非常可怕的。
解决的办法是把该指针初始化为0.


#include<iostream>
using namespace std;
int main()
{
int *p;  //定义了一个指向整型变量的指针p.
p=0;     //将变量p中所保存的内存地址清0.
//这样指针p就不会因为我们的疏忽而随意指向
        //任何一个地址并且修改该地址的值,以上两句可以合并一句

cout<<"p:"<<p<<endl; //查看空指针的地址
return 0;
}


------------------------------------------------------------
堆不像栈,你可以为它其中的某个内存单元命名,为了数据的隐秘
起见,堆中的每个内存单元都是匿名的,因为你必须先在堆中申请
一个内存单元的地址,然后把它保存在一个指针中。这样你只有
使用该指针才可以访问到该内存单元的数据。






-------------------------------------------------------------


内存泄露:
指向该内存区域的指针自动消失了,计算机就再也找不到该区域的内存了,
就好像是丢失了这块内存一样,我们把这种情况叫做内存泄露。这种糟糕的
情况将一直持续到程序结束该区域的内存才能恢复使用。因此假如你不需要
一块内存空间,那么就必须对指向它的指针使用关键字delete。


由于使用new创建的内存空间不会被系统自动释放,因此假如你不去释放它,
那么该区域的内存将始终不能为其他数据所使用,而指向该内存的指针是个局部变量
,当定义该指针的函数结束并返回时,指针也就消失了,那么我们就再也找不到
该块内存区域。


#include<iostream>
using namespace std;
int main()
{
int *p=new int;
delete p;//这将释放指针所指向的内存,而不会释放指针,因此你还可以使用该指针
return 0;


}
-------------------
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
delete p;
p=new int;//可以再次使用指针p
delete p;
return 0;


}


------------------
//指针不能删除两次
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
delete p;
p=new int;
delete p;
delete p; //错误,指针不能删除两次,程序会崩溃
return 0;


}
--------------------
//指针指向0(*p=0)时,指针可以删除两次
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
delete p;
p=new int;
delete p;
p=0;
delete p;
delete p;//正确,指针指向0可以删除两次,程序不会崩溃
return 0;


}


---------------------------------
//总结性例子
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
*p=3600;
cout<<*p<<endl;
delete p;
cout<<*p<<endl;//因为指向的内存空间已释放,将得到一个垃圾值
p=0;//所以在以后的程序设计中最好在指针所指向的内存空间释放后立即给它赋0.
cout<<*p<<endl;
p=new int;
*p=8;
cout<<*p<<endl;
delete p;
return 0;


}


--------------------------------
//内存泄露
//指针变量p只能保存一个地址,对它重新赋值则表示以前的地址被覆盖
//假如该地址的内存空间没有被释放,那么你将无法再次通过指针p访问它
//因为此时的指针变量p记录的事第二块内存的地址,这就是内存泄露。


#include<iostream>
using namespace std;
int main()
{
int *p=new int;
p=new int; //p将记录第二块内存空间地址,第一块内存出现内存泄露
return 0;


}
==============================================================================
//在堆中创建一个对象
//我们既然可以在堆中保存变量,那么就可以保存对象,我们可以将对象保存在堆中,
//然后通过指针来访问


#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=999;}
private:
int i;
};
int main()
{
Human *p; //定义了一个Human类的指针p
p=new Human;//使用new创建一块内存空间,
//它所占用的内存大小为Human类对象的成员变量大小
//为一个int型该对象占用4个字节,并调用构造函数


//对象的地址交给p保存,在堆中创建的对象时匿名的,
//它没有名字,我们只能通过指针来访问它.


return 0;
}


-------------------------------------------------------------
//删除在堆中创建的对象,我们可以直接删除指向该对象
//的指针(指针真的删除了吗?),这样会自动调用对象的析构函数来销毁该对象
//同时释放内存


#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=999;}
~Human(){cout<<"析构函数执行中....\n";}
private:
int i;
};
int main()
{
Human *p; //定义了一个Human类的指针p
p=new Human;//使用new创建一块内存空间,并调用构造函数


delete p;//调用析构函数删除对象

return 0;
}
---------------------------------------------------------------------


//访问对象的数据成员和函数,使用成员运算符“.”
#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=999;}
~Human(){cout<<"析构函数执行中....\n";}
int get()const{return i;} //由于这个成员函数不会修改这个i的值所以在这个函数         //体前加const说明这个函数不会修改这个i值
private:
int i;
};
int main()
{
Human jack;    //定义Human类的对象jack
cout<<jack.get()<<endl; //通过调用对象公有成员函数get()来输出私有数据成员i
Human *p=new Human;
cout<<(*p).get();//也可以使用p->get();->为成员指针运算符
delete p;


return 0;
}




------------------------------------------------------------------


//在构造函数中开辟内存空间
//我们可以将类的数据成员定义为一个指针,然后再构造函数中开辟新空间,
//将该空间的地址赋给指针。而在析构函数中释放该内存
#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=new int(999);}
//由于int是个整型,不是类对象,所以new int(999)不会调用构造函数,
//而是将999这个数值存储到新建的内存区域中并把地址赋给i。
//在构造函数中开辟新空间,将该空间的地址赋给指针
~Human(){cout<<"析构函数执行中....\n";delete i;}
int get()const{return *i;}
private:
int *i;
};
int main()
{

Human *p=new Human;
cout<<(*p).get();//也可以使用p->get();
delete p;


return 0;
}


------------------------------
//对象在栈与堆中的不同:
//一个存储在栈中的对象,如:Human jack;会在超出作用域时,
//比如说遇到右大括号,自动调用析构函数来释放该对象所占用的内存
#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=999;}
~Human(){cout<<"析构函数执行中....\n";}
int get()const{return i;}
private:
int i;
};
int main()
{
Human jack;

return 0;
}
--------------------------------
//对象在栈与堆中的不同:
//而一个存储在堆中的对象,如:new Human; 则需要程序员自行对其所占用的内存进行释放。否则该对象
//所占用的内存直到程序结束才会被系统回收。
#include<iostream>
using namespace std;
class Human
{
public:
Human(){cout<<"构造函数执行中....\n";i=999;}
~Human(){cout<<"析构函数执行中....\n";}
int get()const{return i;}
private:
int i;
};
int main()
{
Human *jack=new Human;
delete jack;    // 手工释放jack对象
return 0;
}




-----------------------------------------------------------------
this指针:
对象在属于自己的每个成员身上写下自己的名字,以证明该成员是自己的成员,而不是
别的成员的,this变量帮助对象做到这一点,this变量记录每个对象的内存地址,然后
通过间接访问运算符->访问该对象的成员


//下面这个例子可以看出this指针和相应的成员地址是一样的
#include<iostream>
using namespace std;
class A
{
public:
int get()const{return i;}
void set(int x){this->i=x;cout<<"this变量保存的内存:\t"<<this<<endl;}
//默认情况下this指针可以省略不写,上面可直接写成i=x;
private:
int i;
};
int main()
{
A a;
a.set(9);
cout<<"对象a的内存地址:\t"<<&a<<endl;
cout<<a.get()<<endl;
A b;
b.set(999);
cout<<"对象b的内存地址:\t"<<&b<<endl;
cout<<b.get()<<endl;


return 0;
}


//这说明this变量记录每个单独的对象的内存地址,而this指针
//则指向每个单独的对象。因此不同的对象输出的this变量的内存
//地址也不同。
//由于this指针保存了对象的地址,因此你可以通过该指针直接读取某个
//对象的数据,现在知道this变量保存的是对象的地址,那么this指针就是
//指向对象的指针,另外this指针的创建和删除由编译器来完成。


---------------------------------------------------------------
指针的常见错误:


前面我们说过删除一个指针后一定要将该指针设置为空指针,这是应为删除该指针只会
释放它所指向的内存空间,不会删除指针,因此这个指针还存在,并且它仍然会指向原来的
内存空间,因此这时如果你再次尝试使用该指针,那么将会导致程序出错。


====================================================================
//指针的加减运算


#include<iostream>
using namespace std;
int main()
{
int *p=new int;
cout<<"指针p保存的空间地址为:\t\t"<<p<<endl;
p++;
cout<<"自加后,指针p保存的空间地址为:\t"<<p<<endl;
p--;
cout<<"自减后,指针p保存的空间地址为:\t"<<p<<endl;
p=p-2;
cout<<"减2后,指针p保存的空间地址为:\t"<<p<<endl;
//这里的减2相当于减2个int型字节即8个字节


return 0;


}


-------------------------------------------------------------
//指针的赋值运算
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
cout<<"p:"<<p<<endl;
int *p1=new int;
cout<<"p1"<<p1<<endl;
p=p1;  //把指针p1的值赋给指针p
cout<<"赋值后.....\n";
cout<<"p:"<<p<<endl;

return 0;


}


--------------------------------------------------------------


//指针的相减运算 
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
cout<<"p:"<<p<<endl;
int *p1=new int;
cout<<"p1"<<p1<<endl;
*p=p-p1;  //用p-p1,将结果保存到p指向的内存空间中
cout<<"两块内存的地址差:\n";
cout<<*p<<endl;//输出28说明两块内存相差28个字节

return 0;


}


-----------------------------------------------------------


//指针的比较运算 
#include<iostream>
using namespace std;
int main()
{
int *p=new int;
cout<<"p:"<<p<<endl;
int *p1=new int;
cout<<"p1"<<p1<<endl;
if(p>p1)
{
cout<<"p大于p1的内存地址。\n";
}
else
cout<<"p小于p1的内存地址。\n";

return 0;


}
====================================================================
常量指针:


//我们可以将指针定义为常量指针,这样该指针不可改变(即自身的值不可改变)但是它指向的
//目标却可以改变,无论这个目标是变量还是对象。
//指针指向变量
#include<iostream>
using namespace std;
int main()
{
int a=3;
int*const p=&a;  //这就定义了一个常量指针p
//这个指针它自身的值是不可改变的,但是它指向的目标
//却是可以改变的
cout<<"a:"<<a<<endl;
a=4;
cout<<"a:"<<a<<endl;//正确输出,即目标可以改变
//p++;//出现错误,显示p是个常量不可改变
return 0;


}


----------------------------------------


//我们可以将指针定义为常量指针,这样该指针不可改变
//指针指向对象


#include<iostream>
using namespace std;
class A
{
public:
int get()const{return i;}
void set(int x){i=x;}
private:
int i;
};
int main()
{
A*p=new A;  //定义一个A类指针p指向创建的A类对象内存空间
cout<<"p:"<<p<<endl;
p=p+1;      //指针p加1相当于加一个A类对象所占的内存空间即4个字节
cout<<"p:"<<p<<endl;
A*const p1=new A;
//p1=p1+1// 编译报错常量指针不可改变
p1->set(11);
cout<<p1->get()<<endl;

return 0;


}


----------------------------------------------------
指向常量的指针:


//该指针指向的目标是不可修改的,但是该指针可以被修改。
//指向常量的指针只是限制我们修改它指向的目标,它自身是可以被修改的。
#include<iostream>
using namespace std;
class A
{
public:
int get()const{return i;}
void set(int x){i=x;}
private:
int i;
};
int main()
{
const A* p1=new A; //定义一个A类对象为常量对象,即其值不可改变,p1指向这个
//常量对象。
cout<<p1->get()<<endl;
//p1->set(11);// 编译报错常量对象不可改变其值


p1=p1+1;// 正确,指针可改变
return 0;


}
------------------------------------------------------


指向常量的常指针:
//它指向的目标是不可修改的,并且该指针也不可以被修改。
//定义一个指向常量的常量指针
#include<iostream>
using namespace std;
class A
{
public:
int get()const{return i;}
void set(int x){i=x;}
private:
int i;
};
int main()
{
const A* const p1=new A;
cout<<p1->get()<<endl;
//p1->set(11);// 编译报错常量对象不可改变其值
//p1=p1+1;//编译出错,指针为常量指针不可改变
return 0;


}
---------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值