【在指针中存储地址】

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++(指针和高级指针)-上篇的更多相关文章

  1. 《C与指针》——高级指针话题

    指针真是让人又爱又恨..... 首先还是先来看一下C语言中的高级指针声明.不要被表面迷惑最重要. /* ** <C和指针>——高级指针话题 */ int i; //定义一个整型变量 int ...

  2. C的指针疑惑:C和指针13(高级指针话题)上

    int *f(); f为一个函数,返回值类型是一个指向整形的指针. int (*f)(); 两对括号,第二对括号是函数调用操作符,但第一对括号只起到聚组的作用. f为一个函数指针,它所指向的函数返回一 ...

  3. C的指针疑惑:C和指针13(高级指针话题)

    传递命令行参数 C程序的main函数具有两个形参.第一个通常称为argc,代表命令行参数的数目. 第二个通常称为argv,它指向一组参数值.由于参数的数目并没有内在的限制,所以argv指向这组参数值( ...

  4. C语言指针的高级操作

    C语言指针的高级操作 指针  指针 在上篇博客中我介绍了C语言指针的最基本操作,那么我在这篇博客中会介绍一下C语言指针的一些骚操作. 指向指针的指针 这名字乍一听有点拗口,再次一听就更加拗口了.先看定 ...

  5. C和指针 第十三章 高级指针话题

    高级声明: int (*f)(); 这里声明有两个括号,第二个括号是函数调用,第一个括号是聚组作用.(*f)是一个函数,所以f是指向返回整型的函数的指针.程序中的每个函数都位于,内存中某个位置,所以存 ...

  6. C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com

    原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | I ...

  7. 联合与枚举 、 高级指针 、 C语言标准库(一)

    1 输入一个整数,求春夏秋冬 1.1 问题 在实际应用中,有的变量只有几种可能取值.如人的性别只有两种可能取值,星期只有七种可能取值.在 C 语言中对这样取值比较特殊的变量可以定义为枚举类型.所谓枚举 ...

  8. 关于空指针NULL、野指针、通用指针 (转)

    reference:https://www.cnblogs.com/losesea/archive/2012/11/16/2772590.html 首先说一下什么是指针,只要明白了指针的含义,你就明白 ...

  9. 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

    浅谈 .NET 中的对象引用.非托管指针和托管指针   目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...

随机推荐

  1. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #14 虚拟存储子系统的调整

    HACK #14 虚拟存储子系统的调整 本节介绍如何使用/proc进行虚拟存储子系统的调整.虚拟空间存储方式在Linux上向应用程序分配内存时,是通过以页面为单位的虚拟存储方式进行的.采用虚拟存储方式 ...

  2. 新手之:SpringBoot ——Reids主从哨兵整合(CentOS7)

    一.Redis主从搭建(一台服务器模拟多个端口) 结构图:) 1.确保安装了Redis,我装在了/opt/redis目录下.可通过"whereis redis-cli"命令查看是否 ...

  3. hadoop学习day1环境配置笔记(非完整流程)

    hdfs的工作机制: 1.客户把一个文件存入hdfs,其实hdfs会把这个文件切块后,分散存储在N台linux机器系统中(负责存储文件块的角色:data node)<准确来说:切块的行为是由客户 ...

  4. Go语言环境安装详细介绍

    工具链介绍 go有两套编译工具链,分别是从plant9移植过来的gc和依赖gcc的gccgo. 官方为gc工具链提供了二进制安装包和源码, 可以根据需要选择一种安装方式.gc工具链对操作系统和CPU类 ...

  5. 【好文转帖】控制反转(IOC)和依赖注入(DI)的区别

    IOC   inversion of control  控制反转 DI   Dependency Injection  依赖注入 要理解这两个概念,首先要搞清楚以下几个问题: 参与者都有谁? 依赖:谁 ...

  6. 开启php的xdebug扩展及phpstorm配置xdebug,chrome调试插件组合

    一. 开启php xdebug扩展      注意:     1. 原生php各版本需对应各自的xdebug版本,可到xdebug上对应下载     2. 若用wampserver等环境,wampse ...

  7. angularjs之$ajax请求

    AngularJS不仅仅只有双向绑定等等功能,还有发送Ajax请求的Api. 效果图: 请求的文件(data.php): <?php $data = [ '股市下跌', '清明小长假结束', ' ...

  8. MyEclipse8.6启动后提示内存不足的解决方案(亲测,完美解决)

    转自:http://www.bubuko.com/infodetail-1625857.html 最近可能由于公司项目大了,启动MyEclipse后经常提示内存不足的警告框,如下: 其实点击close ...

  9. delphi Firemonkey ListView 使用参考

    delphi Firemonkey ListView 使用参考 Tokyo版本 http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Customizin ...

  10. VB中的正则表达式

    RegExp对象提供简单的正则表达式支持功能. RegExp对象的用法:Function RegExpTest(patrn, strng)Dim regEx, Match, Matches ' 建立变 ...