new & delete
在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
既然有了malloc和free为什么C++还要引入new和delete呢?主要有两点考虑- new和delete 是operator;malloc和free是function。一个开销小一个开销大,当需要经常在堆上开辟和释放内存时,程序的执行效率明显不同。
new和delete能够调用构造函数和析构函数开辟和释放对象;而malloc和free想要对类与对象进行操作必须写全套,否则就有内存泄漏的风险。
写个程序,对比一下new和delete;malloc和free的使用。#include <iostream> using namespace std; class Temp{ private: int m_a; int m_b; int m_c; public: Temp(int a, int b, int c) :m_a(a), m_b(b), m_c(c){ cout << "construct"<<endl; } Temp(const Temp & t){ m_a = t.m_a; m_b = t.m_b; m_c = t.m_c; cout << "construct" << endl; } void setT(int a, int b, int c){ m_a = a; m_b = b; m_c = c; cout << "set the value of t" << endl; } void printT(){ cout << "a: " << m_a << " b: " << m_b << " c: " << m_c << endl; } ~Temp(){ cout << "destruct" << endl; } }; int main(){ //C风格的动态内存分配 //1. 变量 int * p = (int*)malloc(sizeof(int)); *p = 100; cout << *p << endl; if (p != NULL){ free(p); } cout << "---------------------" << endl; //2. 数组 int *pp = (int*)malloc(sizeof(int)* 10); for (int i = 0; i < 10; i++){ pp[i] = i; } for (int i = 0; i < 10; i++){ cout << pp[i] << " "; } cout << endl; if (pp != NULL){ free(pp); } cout << "---------------------" << endl; //3. 二维数组 /* 先开辟一个二级指针指向多个一级指针 然后对每个一级指针开辟一块内存空间。 */ int ** ppp = (int **)malloc(sizeof(int *)* 10); for (int i = 0; i < 10; i++){ ppp[i] = (int *)malloc(sizeof(int)* 2); ppp[i][0] = i + 1; ppp[i][1] = i + 2; } for (int i = 0; i < 10; i++){ cout << *ppp[i] << " " << *(ppp[i] + 1) << endl; } for (int i = 0; i < 10; i++){ if (ppp[i] != NULL){ free(ppp[i]); } } free(ppp); cout << "---------------------" << endl; //4. 开辟一个对象 Temp * t = (Temp *)malloc(sizeof(Temp)); t->setT(1, 2, 3); t->printT(); if (t != NULL){ free(t); cout << "clear the value of t" << endl; } cout << "---------------------" << endl; //C++风格 new delete //1. 变量 int *p_ = new int(10); cout << "C++: p:" << *p_ << endl; if (p_ != NULL){ //free(p_); delete p_; } cout << "---------------------" << endl; //2. 数组 int *pp_ = new int[10]; for (int i = 0; i < 10; i++){ pp_[i] = i; } for (int i = 0; i < 10; i++){ cout << pp_[i] << " "; } cout << endl; if (pp_ != NULL){ //free(pp_); delete[] pp_; } cout << "---------------------" << endl; //3.二维数组 int (*ppp_)[5]= new int[5][5]; for (int i = 0; i < 5; i++){ for (int j = 0; j < 5; j++){ ppp_[i][j] = i + j; } } for (int i = 0; i < 5; i++){ for (int j = 0; j < 5; j++){ cout << ppp_[i][j] << " "; } cout << endl; } if (ppp_ != NULL){ delete[] ppp_; } cout << "---------------------" << endl; //4. 对象 Temp * t_ = new Temp(1, 2, 3); t_->printT(); if (t_ != NULL){ delete t_; } cout << "---------------------" << endl; return 0; }
有几个点想提一下:
- malloc和free new和delete除了上述两点可以看做是”一样”的。我们可以用free释放new分配的内存空间。使用delete释放new分配的内存空间。但是这种写法是不符合规范的。
- new分配内存空间赋初值使用();分配多个空间使用[num];delete中释放多个内存空间使用delete[],其他情况使用delete即可。
- 分配内存之后一定要初始化;释放内存前一定要判断指针是否为空。
static
将C++中的static之前先回顾一下C中的static。
在C中:- static修饰局部变量表示在全局区的静态区中开辟一块内存空间。
- static修饰全局变量时,表示该变量只能在该文件中被使用,不能被别的文件访问。
- static修饰函数时,表示该函数只能在该文件中被使用,不能被别的文件访问。
在C++中,上述规则同样试用。但是在C++中,我们多了类的概念,下面就来讲一讲C++类中static的用法。
C++类中的static主要有两种用法:
静态成员变量
静态成员变量是为了数据共享而创造的概念。C++中static成员变量属于类(当然能够被类实例化的对象所调用),但是却不和类存储在一起(static成员变量存放在全局区的静态区)。
定义在类中的static变量和定义在类外的static变量有什么区别呢?我们打个比方,定义在类中的静态成员变量就想是被类”冠名了”一样static属于类,在使用静态成员变量之前要加上冠名商,也就是类名的名字来使用。即静态成员变量的命名空间属于类。//声明 static 数据类型 成员变量; //在类的内部 //初始化 数据类型 类名::静态数据成员=初值; //在类的外部 //调用 类名::静态数据成员 类对象.静态数据成员
用处:
- 最直接的一个例子:定义一个box,要求所有box高度一样。这样只需要定义一个可以共享的变量height就解决问题了。
- 仓库进货模型可以使用静态成员变量来解决。定义一个仓库类,构造函数中,写入进货的数量和重量。存放在静态成员变量中;析构函数中写出货的数量和重量。结合链表实现构造和析构,这样通过两个全局变量就能够管理仓库中货物的数量和重量。同样的例子还有求学生平均分。
静态成员函数
定义和调用
//声明 static 函数声明 //调用 类名::函数调用 类对象.函数调用
- tips
- 静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员, 完成对静态数据成员的封装。
- 静态成员函数只能访问静态数据成员。原因:非静态成员函数,默认传入this指针。而静态成员函数属于类,而不属于对象,没有 this 指针。
从静态成员变量看类中属性和方法的内存分配
注意:为了方便描述,下列叙述并不严谨,但表达的意义理解就好。
类中普通成员变量和类一起存储。
类中静态成员变量存储在全局区的静态区。和类中对象的存储分离。
类中成员函数无论是否静态均与类分离存储。我们知道函数名表示一个指针,占4个字节。尽管如此,函数全部存在代码区,不占用类的内存。那为什么C++这么对类呢?
因为C++的编译器把类翻译成结构体和函数的组合。
总结: static把局部变量变成全局变量,把全局变量变成锁定的变量。
static把类中的成员变成公有的成员,把类中的方法去掉this指针,只能访问类中静态成员变量。
this指针
考虑一个问题:
我们知道类中的成员函数都存放在代码段,只存储一份。那么不同的对象调用同一个函数得到不同的输出。但是该函数没有看到有”输入的参数”,那么不同的输出是怎么来的呢?this的作用
this指针解决的第一个问题就是成员函数如何无条件访问类中属性。
this指针解决的第二个问题就是名字冲突的问题。
this指针解决的第三个问题是成员函数如何返回调用对象自身。this指针的本质
this指针本质上是指向当前对象的一个常指针,每个对象都会有一个this指针。在对象调用成员函数时,会隐式的传入当前对象的地址为this指针初始化。如果希望通过this指针访问类中属性不被修改,可以使用const。具体如下://成员函数如何定义才能将this指针升级为指向常量的常指针? void test() const {} //在小括号和花括号之间加上const
全局函数和局部函数的互换
我们知道全局函数没有this指针传入当前对象;而局部函数有this指针隐式传值,所以两者如果互换,参数要做转换。这里将一个C++编程的原则。我们知道C++是基于值传递的。值传递就要进行拷贝。所以我们往往使用指针和引用避免拷贝。在面对对象的时候更是这样。这个时候局部函数的优势就出来了。使用全局函数,如果想要输出,不可避免的要发生拷贝,且不能直接返回被操作的对象,因为没有this指针。当然如果要强行用全局函数实现也可以,但是跟成员函数比肯定会麻烦不少。使用成员函数返回*this就把当前对象返回了;返回值类型定义为引用,这样就避免了拷贝。并且是匿名引用,使用完毕全部都会被销毁,也不占用内存空间。