C++(指针和高级指针)-上篇
【在指针中存储地址】
int *pAge=nullptr; //将PAge声明为int指针,即用于存储int变量的地址
如果将指针初始化为0或者NUll,以后必须将变量的地址赋给它,如下例代码:
int howOld=;
int *pAge=nullptr;
pAge=&howOld;
【间接运算符(解除引用运算符)】
int howOld=;
int *pAge=nullptr;
pAge=&howOld;
int yourAge;
yourAge=*pAge; //*表示存储在.....处的值。
【通过指针访问指针中存储的内容,指针存储的内容(地址)指向的值,和指针本身的地址】
int howOld=;
int *pAge=nullptr;
pAge=&howOld;
std::cout<<"the address of howOld"<<pAge<<std::endl; //howOld的地址
std::cout<<"the value of howOld"<<*pAge<<std::endl; //howOld的值
std::cout<<"the address of pAge"<<&pAge<<std::endl; //pAge本身的地址
【为何使用指针】
指针最常用于完成如下三项任务。
1.管理堆中的数据
2.访问类的成员和成员函数。
3.按引用将变量传递给函数
【栈和堆】
程序员通常需要处理下述的5个内存区域
1.全局名称空间
2.堆
3.寄存器
4.代码空间
5.栈
局部变量和函数参数存储在栈中,
代码存储在代码空间中
全局变量在全局名称空间中。
寄存器用于内部管理,如跟踪栈顶和指令指针。
余下的几乎所有内存都分配给了堆。堆也被称为自由存储区
(每当函数返回时,都会清理栈,此时,所有局部变量都不在作用域内,从而从栈中删除,。
只有到程序结束后才会清理堆,因此使用完预留的内存后,你需要负责将其释放。让不再需要的信息留在堆中称为内存泄漏)
堆的优点在于,在显示释放前,你预留的内存始终可用。如果在函数中预留堆中的内存,在函数返回后,该内存仍可用。
【使用关键字new】
在C++中,要分配堆中的内存,可使用关键字new,并在它后面指定要为之分配内存对象的类型,让编译器知道需要多少个内存,例如,new unsigned short int从堆中分配2个字节内存,而new long 分配4字节内存。
关键字new 返回一个内存地址,必须将其赋给指针。要在堆中创建一个unsigned short ,可编写如下代码:
unsigned short int *pPointer;
pPointer=new unsigned short int;
当然也可在声明指针的时候初始化它:
unsigned short int *pPointer=new unsigned short int;
无论采取哪种方式,pPointer 都将指向堆中的一个unsigned short int.可像使用指向变量的指针那样使用它,将一个值存储到该内存区域:
*pPointer=;
如果不能从堆中分配内存(因为内存资源有限),将引发异常。
【使用关键字delete】
使用完分配的内存区域后,必须对指针调用delete,将内存归还给堆。别忘了,指针本身为局部变量,这不同于它指向的内存;当申明指针的函数返回时,指针将不再在作用域中,因此被丢弃。然而,使用new关键字分配的内存并不会自动释放,这些内存将不可用,这被称为内存泄露,因此程序结束前,内存不会归还给堆,就像内存从计算机中漏掉了。
要将内存归还给堆,可使用关键字delete,例如
delete pPointer
删除指针时,实际释放了其地址存储在指针中的内存,这被称为将指针指向的内存归还给堆。指针还是指针,可重新给它复制
对指针调用delete时,将释放它指向的内存。如果再次对该指针调用delete,这将导致程序崩溃!删除指针时,应将其设置为NULL,而对空指针调用delete是安全的。
Animal *pDog=new Animal;
delete pDog; //free the memory
pDog=nullptr; //sets pointer to null
delete pDog; //harmless
以下程序Heap演示了如何给一个变量分配堆内存,然后使用并删除它,并再次使用指针
//Heap.cpp
#include<iostream> int main()
{
int localVariable=;
int *pLocal=&localVariable;
int *pHeap=new int;
if(pHeap==nullptr)
{
std::cout<<"Error!No memory for pHeap!";
return ;
}
*pHeap=;
std::cout<<"localVariable: "<<localVariable<<std::endl;
std::cout<<"*pLocal: "<<pLocal<<std::endl;
std::cout<<"pHeap: "<<pHeap<<std::endl;
delete pHeap;
if(pHeap==nullptr)
{
std::cout<<"Error!No memory for pHeap!";
return ;
}
*pHeap=;
std::cout<<"pHeap: "<<pHeap<<std::endl;
delete pHeap;
return ;
}
程序输出如下:
localVariable:5
*pLocal:5
*pHeap:7
*pHeap:9
虽然最后调用delete pHeap是多余的(程序结束时,将自动归还内存),但是显式的释放内存是一个不错的主意,修改或扩展该程序时,这种操作将带来明显的好处
【避免内存泄漏】
第一种内存泄漏是由于:在函数返回值之前没有delete pPointer,当申明指针的函数返回时,指针将不再在作用域中,因此被丢弃。然而,使用new关键字分配的内存并不会自动释放,这些内存将不可用,这被称为内存泄露
另一种内存泄漏是因为:没有释放指针指向的内存就给他重新复制。例如下面的代码:
unsigned short int *pPointer=new unsigned short int;
*pPointer=;
pPointer=new unsigned short int;
*pPointer=;
这样会导致第一个内存区域(值为72)不可用.导致程序在结束之前都无法将其释放
正确的代码如下:
unsigned short int *pPointer=new unsigned short int;
*pPointer=;
delete pPointer
pPointer=new unsigned short int;
*pPointer=;
注意:程序中每个new 调用都必须有对应的delete调用,应跟踪哪个指针指向了内存区域,并使用完后将内存释放,这很重要。
【开发高级指针】
【在堆中创建对象】
定义了类型Cat后,便可以声明一个指向这种对象的指针,并在堆中实例化一个Cat对象,就像在栈中实例化一样,其语法与在堆中分配int相同
Cat *pCat=new Cat;
这将调用默认构造函数-不接受任何参数的构造函数。每当在堆中或栈中创建对象时,都将调用构造函数
【删除对象】
对指向堆中对象的指针调用delete时,将调用对象的析构函数,然后释放内存。这让类有机会执行清理工作,就像销毁栈中的对象一样。
【使用指针访问数据成员】
可以这样访问: (*pCat).GetAge();
也可以这样访问:pCat->GetAge();
【堆中的数据成员】
类可能有一个或多个数据成员为指针,指向堆中的对象。可在构造函数或成员函数中分配内存,并在析构函数中释放内存,例如以下程序
#include<iostream> class SimpleCat
{
public:
SimpleCat();
~SimpleCat();
int GetAge() const {return *itsAge;}
void SetAge(int age){*itsAge=age;} int GetWeight() const {return *itsWeight;}
void SetWeight(int weight){*itsWeight=weight;} private:
int *itsAge;
int *itsWeight;
}; SimpleCat::SimpleCat()
{
itsAge=new int();
itsWeight=new int();
} SimpleCat::~SimpleCat()
{
delete itsAge;
delete itsWeight;
}
int main()
{
SimpleCat *Frisky=new SimpleCat;
std::cout<<"Frisky is "<<Frisky->GetAge()<<" years old\n";
Frisky->SetAge();
std::cout<<"Frisky is "<<Frisky->GetAge()<<" years old\n";
delete Frisky;
system("pause");
return ;
}
第25~29行的析构函数释放分配的内存。在析构函数中,将nullptr赋给这些指针没有任何意义,因为他们将不可访问。对于已经删除的指针,应该将nullptr赋给它,但这是可违反这种规则的唯一地方,虽然遵守这种规则没有任何坏处
(调用方(这里指main())不知道itsAge和itsWeight是指向堆内存的指针,它不断的调用GetAge()和SetAge(),而内存管理的细节隐藏在类中实现)。
this指针:
每个类成员函数都有一个隐藏的参数:this指针,它指向相应的对象。因此,每次调用GetAge()或者SetAge()时,都通过隐藏参数包含指向相应对象的this指针。
this指针指向其函数被调用的对象,通常不需要它,而只是调用函数并设置成员变量,但偶尔需要访问对象本身(可能旨在返回一个指向当前对象的指针),在这种情况下,this指针将很有用。(该指针将在后续讲到)
悬摆指针:
悬摆指针是导致讨厌且难以发现的bug的罪魁祸首之一!!!!
对指针调用delete(从而释放他指向的内存)后,如果没有重新赋值就使用它,将导致悬摆指针。
注意!!!:::对指针调用delete后,千万不要使用它。改指针仍指向原来的内存区域。但编译器可能在这里存储了其他数据;使用该指针会导致程序崩溃。更糟糕的是,程序可能继续运行,但几分钟后崩溃了。所以出于安全考虑。删除指针后,应将其设置为nullptr,这便解除了它的武装。
const指针:
声明指针时,可在类型前,类型后或者两个使用const 。例如下面声明都合法
const int *pOne;
int * const pTwo;
const int * const pThree;
pOne是指向整型常量的指针,即使用该指针不能修改它指向的值。 例如 *pOne=5是非法的
pTwo是指向整型的常量指针,可修改指向的整型变量,但pTwo不能指向其他变量。不能给常量指针重新赋值,例如: pTwo=&x; 是非法的
pThree 是指向整型常量的常量指针,不能修改它指向的值,也不能让它指向其他的变量。
const指针和const成员函数
如果声明了一个指向常量对象的指针,那么使用该指针只能调用常量函数
#include<iostream> class Rectangle
{
public:
Rectangle();
~Rectangle();
void SetLength(int length){itsLength=length;}
int GetLength() const {return itsLength;} void SetWidth(int width){itsWidth=width;}
int GetWidth() const {return itsWidth;} private:
int itsLength;
int itsWidth; }; Rectangle::Rectangle():itsLength(),itsWidth()
{
} Rectangle::~Rectangle()
{
}
int main()
{
Rectangle *pRect=new Rectangle;
const Rectangle *pConstRect=new Rectangle;
Rectangle * const pConstPtr=new Rectangle; std::cout<<"pRect width: "<<pRect->GetWidth()<<" feet\n";
std::cout<<"pConstRect width: "<<pConstRect->GetWidth()<<" feet\n";
std::cout<<"pConstPtr width: "<<pConstPtr->GetWidth()<<" feet\n"; pRect->SetWidth();
//pConstRect->SetWidth(10);
pConstPtr->SetWidth(); std::cout<<"pRect width: "<<pRect->GetWidth()<<" feet\n";
std::cout<<"pConstRect width: "<<pConstRect->GetWidth()<<" feet\n";
std::cout<<"pConstPtr width: "<<pConstPtr->GetWidth()<<" feet\n";
system("pause");
return ;
}
可以看到由于编译器不允许pConstRect调用SetWidth()函数,只能注释掉才能运行。
运行结果如下所示:
pRect width: 5 feet
pConstRect width: 5 feet
pConstPtr width: 5 feet
pRect width: 10 feet
pConstRect width: 5 feet
pConstPtr width: 10 feet
总结:
1.指针可指向整型等简单数据类型,也可指向对象。
2.可在堆中创建对象,并将其删除。如果你声明了一个类,可声明指向其对象的指针,并在堆中实例化对象。
3.类的数据成员可以是指向堆中对象的指针。可以在类的构造函数或其他函数中分配内存,并在析构函数中将其释放。
C++(指针和高级指针)-上篇的更多相关文章
- 《C与指针》——高级指针话题
指针真是让人又爱又恨..... 首先还是先来看一下C语言中的高级指针声明.不要被表面迷惑最重要. /* ** <C和指针>——高级指针话题 */ int i; //定义一个整型变量 int ...
- C的指针疑惑:C和指针13(高级指针话题)上
int *f(); f为一个函数,返回值类型是一个指向整形的指针. int (*f)(); 两对括号,第二对括号是函数调用操作符,但第一对括号只起到聚组的作用. f为一个函数指针,它所指向的函数返回一 ...
- C的指针疑惑:C和指针13(高级指针话题)
传递命令行参数 C程序的main函数具有两个形参.第一个通常称为argc,代表命令行参数的数目. 第二个通常称为argv,它指向一组参数值.由于参数的数目并没有内在的限制,所以argv指向这组参数值( ...
- C语言指针的高级操作
C语言指针的高级操作 指针 指针 在上篇博客中我介绍了C语言指针的最基本操作,那么我在这篇博客中会介绍一下C语言指针的一些骚操作. 指向指针的指针 这名字乍一听有点拗口,再次一听就更加拗口了.先看定 ...
- C和指针 第十三章 高级指针话题
高级声明: int (*f)(); 这里声明有两个括号,第二个括号是函数调用,第一个括号是聚组作用.(*f)是一个函数,所以f是指向返回整型的函数的指针.程序中的每个函数都位于,内存中某个位置,所以存 ...
- C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com
原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | I ...
- 联合与枚举 、 高级指针 、 C语言标准库(一)
1 输入一个整数,求春夏秋冬 1.1 问题 在实际应用中,有的变量只有几种可能取值.如人的性别只有两种可能取值,星期只有七种可能取值.在 C 语言中对这样取值比较特殊的变量可以定义为枚举类型.所谓枚举 ...
- 关于空指针NULL、野指针、通用指针 (转)
reference:https://www.cnblogs.com/losesea/archive/2012/11/16/2772590.html 首先说一下什么是指针,只要明白了指针的含义,你就明白 ...
- 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包
浅谈 .NET 中的对象引用.非托管指针和托管指针 目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...
随机推荐
- 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #14 虚拟存储子系统的调整
HACK #14 虚拟存储子系统的调整 本节介绍如何使用/proc进行虚拟存储子系统的调整.虚拟空间存储方式在Linux上向应用程序分配内存时,是通过以页面为单位的虚拟存储方式进行的.采用虚拟存储方式 ...
- 新手之:SpringBoot ——Reids主从哨兵整合(CentOS7)
一.Redis主从搭建(一台服务器模拟多个端口) 结构图:) 1.确保安装了Redis,我装在了/opt/redis目录下.可通过"whereis redis-cli"命令查看是否 ...
- hadoop学习day1环境配置笔记(非完整流程)
hdfs的工作机制: 1.客户把一个文件存入hdfs,其实hdfs会把这个文件切块后,分散存储在N台linux机器系统中(负责存储文件块的角色:data node)<准确来说:切块的行为是由客户 ...
- Go语言环境安装详细介绍
工具链介绍 go有两套编译工具链,分别是从plant9移植过来的gc和依赖gcc的gccgo. 官方为gc工具链提供了二进制安装包和源码, 可以根据需要选择一种安装方式.gc工具链对操作系统和CPU类 ...
- 【好文转帖】控制反转(IOC)和依赖注入(DI)的区别
IOC inversion of control 控制反转 DI Dependency Injection 依赖注入 要理解这两个概念,首先要搞清楚以下几个问题: 参与者都有谁? 依赖:谁 ...
- 开启php的xdebug扩展及phpstorm配置xdebug,chrome调试插件组合
一. 开启php xdebug扩展 注意: 1. 原生php各版本需对应各自的xdebug版本,可到xdebug上对应下载 2. 若用wampserver等环境,wampse ...
- angularjs之$ajax请求
AngularJS不仅仅只有双向绑定等等功能,还有发送Ajax请求的Api. 效果图: 请求的文件(data.php): <?php $data = [ '股市下跌', '清明小长假结束', ' ...
- MyEclipse8.6启动后提示内存不足的解决方案(亲测,完美解决)
转自:http://www.bubuko.com/infodetail-1625857.html 最近可能由于公司项目大了,启动MyEclipse后经常提示内存不足的警告框,如下: 其实点击close ...
- delphi Firemonkey ListView 使用参考
delphi Firemonkey ListView 使用参考 Tokyo版本 http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Customizin ...
- VB中的正则表达式
RegExp对象提供简单的正则表达式支持功能. RegExp对象的用法:Function RegExpTest(patrn, strng)Dim regEx, Match, Matches ' 建立变 ...