接着上一篇的内容,本篇是动态内存管理和类,一方面是通过动态内存分配的方式创建和释放类对象,这涉及到动态的对象创建和释放的过程。进而讨论到重载new
和delete
关键字。
啃书《C++ Primer Plus》 动态内存管理(上) new和delete的使用
啃书《C++ Primer Plus》 面向对象部分 动态内存管理(下) 动态成员管理
《动态内存管理》内容思维导图如下:
动态内存和类
动态的对象
使用new和delete关键字可以分配释放内置类型的动态变量,当然也可分配释放类的动态对象。
动态对象的创建和释放
使用new关键字创建动态对象时,形式与创建内置类型变量类似:
类指针 指针名称 = new 类名;
类指针 指针名称 = new 类名(参数列表);
与内置类型不同的是,对象的初始化由其构造函数完成。因此,当new一个对象时,需要根据参数列表选择其对应的构造函数进行初始化。特别的,对于第一种写法,程序将调用其无参构造函数。
来看个粟子:
using namespace std;
class A
{
public:
A(){cout << "hello world" << endl;}
A(int k){cout << k << endl;}
};
int main()
{
A *p1 = new A;
A *p2 = new A();
A *p3 = new A(10086);
}
结果如下:
另外,使用new[]创建对象数组时,将对全体成员默认调用无参构造函数。当然,也可以通过大括号指定每一个成员调用哪一个构造函数。
看下面的例子:
class A
{
public:
A(){cout << "hello world" << endl;}
A(int k){cout << k << endl;}
};
int main()
{
cout << "object array a:" << endl;
A * a = new A[3];
cout << endl << "object array b" << endl;
A * b = new A[3]{A(1),A(3),A(5)};
}
运行结果:
对于delete在此处的使用,与在内置类型那里的使用并没有什么不同。这里就不赘述了,我们将delete加入上面的程序,将他们补充完全:
using namespace std;
class A
{
public:
A(){cout << "hello world" << endl;}
A(int k){cout << k << endl;}
};
int main()
{
A *p1 = new A;
A *p2 = new A();
A *p3 = new A(10086);
delete p1,p2,p3;
}
class A
{
public:
A(){cout << "hello world" << endl;}
A(int k){cout << k << endl;}
};
int main()
{
cout << "object array a:" << endl;
A * a = new A[3];
cout << endl << "object array b" << endl;
A * b = new A[3]{A(1),A(3),A(5)};
delete[] a;
delete[] b;
}
new与delete关键字的重载
方才介绍了对象的动态创建和释放,现在有必要说明在对象的动态创建和释放过程中的步骤。
在对象创建时:
- 调用
operator new
函数分配大小合适的内存 - 调用对应的构造函数对对象进行构造
在对象释放时:
- 调用对象的析构函数进行析构
- 调用
operator delete
函数对内存进行释放
可以看到,对象的创建和释放并非我们想象那般一步到位的,而是将对象的空间管理和内容管理分而治之。
在空间管理部分使用的operator new
和operator delete
函数,就是下面我们要介绍的重点——重载他们。
在程序全局,会存在一个默认的operator new
和operator delete
(当然还包括 operator new[]
和 operator delete[]
)
现在我们可以在类内重载这两个函数,重新规划为对象分配内存的行为。在重载之前,我们需要了解一下它们的函数原型
void * operator new(size_t);
void operator delete(void *,size_t);//类作用域中的版本
void operator delete(vodi *)//全局作用域中的版本
其中,size_t是一个无符号整型,表示创建/释放对象的大小。另外,operator new
函数需要返回一块已经分配好内存的地址
类作用域内重载
下面,我们就来在类内重载这两个函数:
class A
{
public:
A(){cout << "constructing" << endl;} //A的无参构造函数
~A(){cout << "destructing" << endl;} //A的析构函数
void * operator new(size_t size) //重载new
{
cout << "allocating ~" << endl;
return new char[size]; //分配
}
void operator delete(void * p,size_t size)
{
cout << "freeing~" << endl;
delete p;
}
};
int main()
{
A * p = new A();
delete p;
}
运行结果:
上面的例子中,我使用new char[]的方式创建了相应大小的内存块,当然你也可以选择使用malloc
来完成这个工作。
除了重载默认模式new,还可以在参数列表中加入新的参数。但是不论如何,首个参数必须是size_t
class A
{
public:
A(){cout << "constructing" << endl;}
~A(){cout << "destructing" << endl;}
void * operator new(size_t size,string name) //增添了名称参数
{
cout << name << " is using operator new " << endl;
cout << "allocating ~" << endl;
return new char[size];
}
void operator delete(void * p,size_t size)
{
cout << "freeing~" << endl;
delete p;
}
};
int main()
{
A * p = new("jack") A();
delete p;
}
运行结果:
全局重载
对new和delete的重载,不仅在类作用域,也可以发生在全局作用域内。
using namespace std;
void * operator new(size_t size)//重载new
{
cout << "global operator new" << endl;
return malloc(size);
}
void operator delete(void * p)//重载delete
{
cout << "global operator delete" << endl;
free(p);
}
class A
{
public:
A(){cout << "the constructor in class A" << endl;}
~A(){cout << "the destructor in class A" << endl;}
};
int main()
{
/*使用动态内存创建并释放对象*/
cout << "object of class A" << endl;
A * pa = new A();
delete pa;
/*使用动态内存创建并释放内置类型*/
cout << endl << "integer" << endl;
int * pi = new int(2);
delete pi;
}
运行结果:
(new[]和delete[]关键字同理,在这里就不做演示了,希望读者可以亲自尝试 )
new/delete的选择
当使用new动态分配一个对象时,会现在类作用域范围内寻找是否存在operator new函数,若没有,则在全局寻找,若还是没有,则使用默认的模板。(delete释放内存时同理)
我们在上面程序A类中添加new和delete的重载,再运行程序:
class A
{
public:
A(){cout << "the constructor in class A" << endl;}
~A(){cout << "the destructor in class A" << endl;}
void * operator new(size_t size)
{
cout << "operator new in class A" << endl;
return malloc(size);
}
void operator delete(void * p,size_t size)
{
cout << "operator delete in class A" << endl;
free(p);
}
};
运行结果:
可以看到,此时,在分配对象空间时,使用的类作用域内的版本,而分配整型空间时,使用的是全局作用域的版本。
定位new在类作用域中重载
啃书系列往期博客
语言基础部分:
- 啃书《C++ Primer Plus》之 C++ 函数指针
- 啃书《C++ Primer Plus》之 C++ 名称空间1
- 啃书《C++ Primer Plus》之 C++ 名称空间2
- 啃书《C++ Primer Plus》之 C++ 引用
- 啃书《C++ Primer Plus》之 const修饰符修饰 类对象 指针 变量 函数 引用
- 啃书《C++ Primer Plus》之 枚举 内容大全
面向对象部分: