文档章节

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/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大神,都在看Spring Boot和Spring Cloud的书?

如果你是一名Java开发人员,并且最近正打算学习Spring Boot和Spring Cloud框架并寻找一些关于它们的最好的书籍,那么,你今天就来对地方了。 本文,我们将讨论一些学习Spring Boot和Spring ...

Java小铺
17分钟前
6
0
springboot logback日志配置

springboot 如果不使用外部tomcat的话,日志是需要自己配置的,不然的话就只有控制台的日志,但是日志又是我们在项目上了生产环境,出问题时,检查问题的唯一途径,所以我们要配置详细的日志...

曾大大胖
18分钟前
2
0
Linux服务器集体篡改时间的方法

Red Hat 虚拟化课程RH318,中小型公司使用的话,感觉可以匹敌OpenStack。手头上有一个VMware的映像,RHEV 3.5版的,只能把系统时间调整到2016年才能使用。Red Hat的RHEV已经更新到4.1版,不过...

大别阿郎
18分钟前
1
0
Tomcat启动异常:java.lang.ClassNotFoundException

警告: Name = mysqlDataSource Property maxActive is not used in DBCP2, use maxTotal instead. maxTotal default value is 8. You have set value of "100" for "maxActive" property, wh......

hengbao5
19分钟前
1
0
GO错误的一些处理(defer,recover,panic)

package main import("fmt""errors")func main() {num := 10fmt.Printf("num的类型%T, num的值%v, num的地址%v\n", num, num, &num)num2 := new(int) //返回一个指针//num...

汤汤圆圆
28分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部