啃书《C++ Primer Plus》 面向对象部分 动态内存管理(中) 动态对象的创建 重载new和delete

接着上一篇的内容,本篇是动态内存管理和类,一方面是通过动态内存分配的方式创建和释放类对象,这涉及到动态的对象创建和释放的过程。进而讨论到重载newdelete关键字。

啃书《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关键字的重载

方才介绍了对象的动态创建和释放,现在有必要说明在对象的动态创建和释放过程中的步骤。
在对象创建时:

  1. 调用operator new函数分配大小合适的内存
  2. 调用对应的构造函数对对象进行构造

在对象释放时:

  1. 调用对象的析构函数进行析构
  2. 调用operator delete函数对内存进行释放

可以看到,对象的创建和释放并非我们想象那般一步到位的,而是将对象的空间管理和内容管理分而治之。
在空间管理部分使用的operator newoperator delete函数,就是下面我们要介绍的重点——重载他们。

在程序全局,会存在一个默认的operator newoperator 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在类作用域中重载

啃书系列往期博客

语言基础部分:

面向对象部分:

猜你喜欢

转载自blog.csdn.net/wayne_lee_lwc/article/details/106139022