文档章节

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

wqli
 wqli
发布于 2016/05/14 12:27
字数 1738
阅读 67
收藏 5

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++基础教程之C/C++区别

C/C++基础教程之C/C++区别 这是C++教程得第一步,后续会持续更新哦!欢迎新手(具有C基础),老鸟可绕道,可指导。 C++标准输入和输出 C++标准输入和输出分别是cin和cout,用法非常简单 cin>>n...

这个人很懒什么都没留下
09/01
0
0
c语言基础学习11_项目实战:IDE(集成开发环境)

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

黑泽明军
01/29
0
0
C++基础教程之构造函数与析构函数

构造函数 当我们需要在对象创建时初始化一些数据的时候,我们不可能提供一个普通的成员方法供程序猿在对象创建后调用。因为如果程序猿故意或者无意间忘记了调用该方法,就可能导致程序出现偏...

这个人很懒什么都没留下
09/08
0
0
C++11 中值得关注的几大变化

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

雅各宾
2014/01/17
0
0
C++:一段代码,了解拷贝构造函数、move构造函数、拷贝赋值函数、move赋值函数、右值引用

本文纯粹是个整理。 以下代码和图片来自C++ 11右值引用。 其他: 什么是左值、右值: C++左值与右值之道 C++左值与右值之间共同与不同点解析 C/C++左值性精髓(一) 左值的前世今生 C/C++左值...

樂天
2015/06/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

码云项目100,水一发

简单回顾一下: 早期构想最多的,是希望能将PHP一些类和编码分区做得更细,所以很多尝试。但不得不说,PHP的功能过于单一,是的,也许写C/C++扩展,可以解决问题,那我为什么不用C#或者Golan...

曾建凯
今天
1
0
Spring应用学习——AOP

1. AOP 1. AOP:即面向切面编程,采用横向抽取机制,取代了传统的继承体系的重复代码问题,如下图所示,性能监控、日志记录等代码围绕业务逻辑代码,而这部分代码是一个高度重复的代码,也就...

江左煤郎
今天
4
0
eclipse的版本

Eclipse各版本代号一览表 Eclipse的设计思想是:一切皆插件。Eclipse核心很小,其它所有功能都以插件的形式附加于Eclipse核心之上。 Eclipse基本内核包括:图形API(SWT/Jface),Java开发环...

mdoo
今天
3
0
SpringBoot源码:启动过程分析(一)

本文主要分析 SpringBoot 的启动过程。 SpringBoot的版本为:2.1.0 release,最新版本。 一.时序图 还是老套路,先把分析过程的时序图摆出来:时序图-SpringBoot2.10启动分析 二.源码分析 首...

Jacktanger
今天
6
0
小白带你认识netty(二)之netty服务端启动(上)

上一章 中的标准netty启动代码中,ServerBootstrap到底是如何启动的呢?这一章我们来瞅下。 server.group(bossGroup, workGroup);server.channel(NioServerSocketChannel.class).optio...

天空小小
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部