C++程序设计:原理与实践读书笔记(第十二章)

C++语言用称为new的运算符将“自由空间”(又称为“堆”)变为可用状态。例如:

double *p = new double[4];    //在自由空间中分配4个double

这段代码要求C++运行时系统在自由空间中分配四个double,并将指向第一个double的指针返回。

我们使用运算符new来请求系统从自由空间中分配内存:

  • 运算符new返回一个指向分配的内存的指针。
  • 指针的值是分配的内存的首字节地址。
  • 一个指针指向一个特定类型的对象。
  • 一个指针并不知道它指向多少个元素。

一如以往,我们必须要保证在使用对象之前为它赋一个值;也就是说,我们希望确保指针被初始化,并且指向的对象也被初始化。

double* p0;                    //未初始化,可能产生问题
double* p1 = new double;       //得到(分配)一个未初始化的double
double* p2 = new double{5.5};  //得到一个初始化为5.5的double
double* p3 = new double[5];    //得到(分配)5个未初始化的double

当我们定义自己的类型时,可以更好的控制初始化。如果类型X有一个默认构造函数,我们会得到:

X* px1 = new X;            //一个默认初始化的X
X* px2 = new X[17];        //17个默认初始化的X

如果类型Y有一个构造函数,但不是默认构造函数,我们需要显示初始化:

Y* py1 = new Y;                //错误:无默认构造函数
Y* py2 = new Y{13};            //正确:初始化为Y{13}
Y* py3 = new Y[13];            //错误:无默认构造函数
Y* py4 = new Y[17]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

为new指定很长的初始化列表可能不太实用,而少量元素通常实用这种类型。

如果你没有其他指针来初始化一个指针,那么使用空指针nullptr:

double *p0 = nullptr;    //空指针

当0值被赋值给一个指针时,它被称为空指针。通常我们通过检测指针是否为nullptr来检查一个指针是否有效:

if(p0 != nullptr)        //认为p0有效

实际上不必明确提及nullptr,因为if语句会检测它的条件是否是nullptr:

if(p0)            //认为p0有效;等价于p0!=nullptr

名字nullptr表示空指针是C++11的新特性。旧代码中通常使用0或NULL代替nullptr。

delete有两种形式:

  • delete p 释放new分配给单个对象的内存
  • delete[] p 释放new分配给对象数组的内存

删除空指针是无害的!

一个对象离开作用域时会隐式调用析构函数。构造函数确保一个对象被正确创建并初始化。与之相反,析构函数确保一个对象销毁前被正确清理。

作为一个经验法则:如果你又一个类带有virtual函数,则它也需要一个virtual析构函数,原因是:

  • 如果一个类有virtual函数,它很有可能作为一个基类使用;
  • 且如果它是一个基类,它的派生类很可能使用new来分配;
  • 且如果派生类对象使用new来分配,并通过基类指针来操作;
  • 那么它很可能也是通过基类指针来进行delete操作的。

类型void*的含义是“指向编译器不知道类型的内存空间”。当我们想在两段代码之间传输一个地址,它们确实不知道对方的类型时,就可以使用void*。指向任何对象类型的指针都可以赋予void*,例如:

void* pv1 = new int;            //正确:int*转换为void*
void* pv2 = new double[10];     //正确:double*转换为void*

由于编译器不知道void*是什么,我们必须告诉它:

void f(void* pv)
{
    void* pv2 = pv;        //正确拷贝
    double* pd = pv;       //错误,不能将void*转换为double*
    *pv = 7;               //错误,不能解引用一个void*
    
    PV[2] = 9;             //错误:不能对void*进行下标操作
    int* pi = static_cast<int*>(pv);        //正确,显式类型转换
    //...
}

static_cast可以用于在两种相关指针类型之间进行强制转换,例如void*与double*。这样的操作称为显式类型转换。

C++提供两个比static_cast更具潜在危险的转换操作:

  • reinterpret_cast可以在两个不相关的类型之间转换,例如int和double。
  • const_cast可以“去掉const”。

我们可以将一个引用看做一个自动解引用的不可变指针或一个对象的替代名字。指针和引用有以下几个方面不同:

  • 为一个指针赋值会改变指针自身的值(不是指针指向的值)。
  • 为了得到一个指针,你通常需要使用new或&。
  • 为了访问一个指针指向的对象,你可以使用*或[]。
  • 为一个引用赋值会改变引用指向的值(不是引用自身的值)。
  • 在初始化一个引用之后,你不能让引用指向其他对象。
  • 为引用赋值执行深拷贝(赋值给引用的对象);为指针赋值不是这样(赋值给指针自身)。
  • 注意避免空指针。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 本书是经典程序设计思想与C++开发实践的完美结合,是C++之父回归校园后对C++编程原理和技巧的全新阐述。书全面地介绍了程序设计基本原理,包括基本概念、设计和编程技术、语言特性以及标准库等,教你学会如何编写具有输入、输出、计算以及简单图形显示等功能的程序。此外,本书通过对C++思想和历史的讨论、对经典实例(如矩阵运算、文本处理、测试以及嵌入式系统程序设计)的展示,以及对C语言的简单描述,为你呈现了一幅程序设计的全景图。   ·C++初学者的权威指南。无论你是从事软件开发还是其他领域的工作,本书将为你打开程序开发之门。   ·高级程序员的必备参考。通过观察程序设计大师如何处理编程的各种问题,使你获得新的领悟和指引。   ·全面阐释C++基本概念和技术。与传统的C++教材相比,本书对基本概念和技术的介绍更为深入,为你编写实用、正确、易维护和有效的代码打下坚实的基础。   ·强调现代C++编程风格。本书从开篇就介绍现代C++程序设计技术,并揭示了大量关于如何使用C++标准库来简化程序设计原理,使你快速掌握实用编成技巧。 图书目录   出版者的话   译者序   前言   第0章 致读者   第1章 计算机、人与程序设计   第一部分 基本知识   第2章 Hello,World!   第3章 对象、类型和值   第4章 计算   第5章 错误   第6章 编写一个程序   第7章 完成一个程序   第8章 函数相关的技术细节   第9章 类相关的技术细节   第二部分 输入和输出   第10章 输入/输出流   第11章 定制输入/输出   第12章 一个显示模型   第13章 图形类   第14章 设计图形类   第15章 绘制函数图和数据图   第16章 图形用户界面   第三部分 数据结构和算法   第17章 向量和自由空间   第18章 向量和数组   第19章 向量、模板和异常   第20章 容器和迭代器   第21章 算法和映射   第四部分 拓宽视野   第22章 理念和历史   第23章 文本处理   第24章 数值计算   第25章 嵌入式系统程序设计   第26章 测试   第27章 C语言   术语表   参考书目   第五部分 附录?   附录A C++语言概要   附录B 标准库概要   附录C Visual Studio简要入门教程   附录D 安装FLTK   附录E GUI实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值