文档章节

C++基础知识学习:new与delete表达式

wqli
 wqli
发布于 2016/05/14 12:27
字数 1738
阅读 66
收藏 5
点赞 2
评论 0

C++中分配的内存大致有三类:静态存储区,栈内存和堆内存

其中,静态存储区是在程序编译阶段就已经分配好的,用于全局变量,static变量等;堆栈是比较常用的对象存储方式。

new和delete是C++中程序员申请和撤销堆内存的唯一方式(据我所知)。

1. 创建对象

无论是定义内置类型变量还是类类型变量,都必须指定其对应的数据类型和名字。

动态创建对象只需指定其数据类型,而不必为该对象命名。

也就是说,变量定义得到的对象可以通过额外定义的指针或其对象名本身来访问,而动态创建方式得到的对象只能通过返回的指针来访问,因为该对象并没有名字。

2. 初始化

a. 可以在变量定义或是动态创建的阶段初始化对象,例如:

int i(222); // definition, initialized
int *pi = new int(222) // dynamically, initialized

b. 在这还要说一说变量定义时的默认初始化问题:

内置类型的变量初始化与其定义位置有关:函数外的自动初始化为零,而函数内的没有初始化。对于类类型的变量,程序总是会调用默认构造函数来初始化,这个默认构造函数可以是系统自动生成的,也可以是程序员定义的。如果没有默认构造函数,那么该类型变量的定义也就不能采用默认初始化方式,必须提供显式的初始化式。

动态创建的对象默认初始化与函数内变量定义的初始化方式相同,例如:

string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int

c. 动态创建对象的值初始化(value-initialize)

这种值初始化不能用于变量定义,而只能用于动态创建方式。

string *ps2 = new string(); // initialized to empty string
int *pi = new int(); // pi points to an int value-initialized to 0

值初始化表明程序员想要做初始化,但并未提供特定的初值。实际上,对于提供了默认构造函数的类类型(如string),没有必要对其对象进行值初始化:无论程序是明确地不初始化还是要求进行值初始化,都会自动调用其默认构造函数初始化该对象。值初始化真正有用的是对于内置类型。另外需要注意的一点是,值初始化的 () 语法必须置于类型名后面,而不是变量名后面,否则得到的是一个函数声明,如下:

int myValue(); // not value-initialized int variable, but a function named myValue
int *myPtr = new int(); // correct! a value-initialized int object

对于没有默认构造函数的类类型,无论是变量定义还是动态创建对象都必须采用显式初始化。

3. 撤销对象

动态创建的对象用完后,程序员必须显式地将该对象占用的内存返还给自由存储区。C++提供了delete表达式释放指针所指向的地址空间。

delete myPtr;

上述语句释放 myPtr 指向的 int 型对象所占用的内存。

C++没有明确定义如何释放指向不是用 new 分配的内存地址的指针。此外,编译器通常不能断定一个指针究竟指向什么类型的对象,因此如果调用delete 企图释放指向栈内存地址的指针时,编译器并不会报错,但请尽量不要依赖于该未定义的行为。

在C++中 delete 一个零值指针是合法且安全的,但实际上毫无意义。

悬垂指针(dangling pointer)

删除了指针所指向的对象后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象实际已经不存在了。悬垂指针往往导致程序错误,并且很难检测。因此,在调用delete 释放指针所指对象内存后应该立刻将指针置零。

4. Const 对象的动态分配和回收

const 对象,必须在定义阶段或动态创建阶段进行初始化,并且初始化之后其值不能再修改。

与其他 const 对象的地址一样,由于 new 返回的地址上存放的是 const 对象,因此该地址只能赋给指向 const 的指针。

// allocate and initialize a const object
const int *pci = new const int(222); // initialize to 222
const int *pci2 = new const int();   // initialize to 0

尽管程序员不能改变 const 对象的值,但可撤销对象本身。

delete pci; // ok: delete a const object

三种常见的程序错误都与动态内存分配相关

  • 删除动态分配内存失败,称为内存泄漏(memory leak)
  • 读写已删除的对象
  • 对同一个内存空间使用两次 delete 表达式。当两个指针指向同一个动态创建的对象,删除时就会发生错误。第二个指针的 delete 操作往往会破坏自由存储区。

个人实践部分:

#include <iostream>
#include <cstring>
using namespace std;

int main(void)
{
    string str = "hello str";
    // strNew points to dynamically allocated,
    // initialized to empty string
    string *strNew = new string;
    
    cout<<"str object address: "<<&str<<endl;
    cout<<"strNew pointer itself address: "<<&strNew<<endl;
    cout<<"strNew pointer to address: "<<strNew<<endl;
    
    // assignment
    *strNew = "hello strNew";
    cout<<"strNew pointer to address: "<<strNew<<endl;
    
    // free memory
    delete strNew;
    cout<<"strNew pointer to address: "<<strNew<<endl;
    strNew = NULL;
    
    // point to other object
    strNew = &str;
    cout<<"strNew pointer to address: "<<strNew<<endl;
    
    
    const int cvalue(10);
    // iptr points to a const int object
    const int *iptr = new const int(222);
    cout<<"iptr value: "<<*iptr<<endl;
    delete iptr;
    iptr = NULL;
    
    iptr = &cvalue;
    cout<<"iptr value: "<<*iptr<<endl;
    
    return 0;
}

一次运行的结果如下:

str object address: 0x28ff24
strNew pointer itself address: 0x28ff20
strNew pointer to address: 0x602f70
strNew pointer to address: 0x602f70
strNew pointer to address: 0x602f70
strNew pointer to address: 0x28ff24
iptr value: 222
iptr value: 10

程序中间将原来指向 new 创建的对象的指针重定向到一般的变量,可以看到指针存放地址的改变。另外需要注意,在释放 new 对象之前不要将指针重新指向某个其他对象,这样会导致原来动态创建的对象没有指针指向它,无法释放内存空间。

学习书籍:C++ Primer 中文第四版,5.11节

© 著作权归作者所有

wqli
粉丝 3
博文 72
码字总数 66028
作品 0
长宁
c语言基础学习11_项目实战:IDE(集成开发环境)

============================================================================= ============================================================================= 涉及到的知识点有: 一......

黑泽明军
01/29
0
0
如何自学成为C/C++程序员大牛

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
05/11
0
0
C++11 中值得关注的几大变化

源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 (and Why You Should Care),赖勇浩做了一个中文翻译在这里。所以,我就不翻译了,我在这里仅对文中提到的这些变...

雅各宾
2014/01/17
0
0
Java程序员如何高效而优雅地入门C++

Java程序员如何高效而优雅地入门Cpp,由于工作需要,需要用C++写一些模块。关于C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 我也算是老手,但也还需要心生敬畏,本文会从...

小欣妹妹
04/23
0
0
浅谈MFC内存泄露检测及内存越界访问保护机制

本文所有代码均在VC2008下编译、调试。如果您使用的编译器不同,结果可能会有差别,但本文讲述的原理对于大部分编译器应该是相似的。对于本文的标题,实在不知道用什么表示更恰当,因为本文不...

songchang
2012/09/28
0
0
C语言编程基础学习如何定义一维数组和二维数组

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
04/01
0
0
旅行,说走就走 Help? [C++数据类型和表达式]

旅行,说走就走 Help? [C++数据类型和表达式] 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 乐天派。我却喜欢和老妈说“老妈小时候喜欢羡慕...

泥沙砖瓦浆木匠
2014/08/29
0
0
C语言编程学习运算符的优先级和结合性

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
05/25
0
0
C语言编程学习:写的秒速计算四则混合运算项目

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
06/05
0
0
C语言/C++编程学习三种循环用法和区别,你还不明白吗?

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
06/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Python PIPEs

https://www.python-course.eu/pipes.php https://www.tutorialspoint.com/python/os_pipe.htm

zungyiu
4分钟前
0
0
gRPC学习笔记

gRPC编程流程 1. proto文件定义 proto文件用于定义需要通过gRPC生成的接口,可以理解为接口定义文档 2. 通过构建工具生成服务基类代码-Maven或Gradle 3. 服务端开发 服务端实现类须实现通过构...

OSC_fly
23分钟前
0
0
Docker Mac (三) Dockerfile 及命令

Dockerfile 最近学习docker的时候,遇到一件怪事,关于docker镜像可能会被破坏,还不知道它会有此措施 所以需要了解构建Dockerfile的正确方法 Dockerfile是由一系列命令和参数构成的脚本,这些命...

___大侠
50分钟前
0
0
Android Studio+NDK+Cmake 移植FFmpeg-4.0.2命令行工具

一、编译 参考大神的帖子,亲测一次编译成功:https://blog.csdn.net/bobcat_kay/article/details/80889398 鉴于以前查文档的经验,这里附上编写例子的时间:2018年7月22日 我用的是ubantu,...

她叫我小渝
50分钟前
0
0
mysql创建数据库

登录MYSQL mysql -u root -p 脚本创建数据库WeChat,并制定默认的字符集是utf8mb4。 CREATE DATABASE Wechat DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 授权 grant all......

niithub
今天
0
0
svn: Unable to connect to a repository URL 的解决方案

错误图示: 解决办法:清除本地保存的授权信息; 1:右键点击本地文件夹,选择设置; TortoiseSVN -> Settings 2:在弹出的对话框中选择 Saved Data, 右侧选择:授权地方清理所有。 然后点确...

宁哥实战课堂
今天
1
0
sleep与wait的区别

Thread.sleep(XXX)方法消耗CPU吗? 这个知识点是我之前认识一直有错误的一个知识点,在我以前的认识里面,我一直认为Thread.sleep(1000)的这一秒钟的时间内,线程的休眠是一直占用着CPU的时间...

码代码的小司机
今天
1
0
20位活跃在Github上的国内技术大牛 leij 何小鹏 亚信

本文列举了20位在Github上非常活跃的国内大牛,看看其中是不是很多熟悉的面孔? 1. lifesinger(玉伯) Github主页: https://github.com/lifesinger 微博:@ 玉伯也叫射雕 玉伯(王保平),...

海博1600
今天
1
0
Mybatis收集配置

一、Mybatis取Clob数据 1、Mapper.xml配置 <resultMap type="com.test.User" id="user"> <result column="id" property="id"/> <result column="json_data" property="jsonData" ......

星痕2018
今天
1
0
centos7设置以多用户模式启动

1、旧版本linux系统修改inittab文件,在新版本执行vi /etc/inittab 会有以下提示 # inittab is no longer used when using systemd. # # ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON......

haha360
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部