C++ new的三种用法
C++ new的三种用法
参考文章:
https://www.jb51.net/article/41524.htm
https://blog.csdn.net/aoeaoao/article/details/124553631
概述
三种用法包括:plain new、nothrow new、placement new。
plain new
顾名思义就是最普通的new的使用,在C++中定义如下:
void* operator new(std::size_t) throw(std::bad_alloc);
void operator delete(void *) throw();
TIPS:plain new在分配失败下会抛出异常(std::bad_alloc),而不是返回NULL,即通过判断返回值是否为NULL,来断定是否分配失败是无效的。
实例
#include "stdafx.h"
#include <iostream>
using namespace std;
char *GetMemory(unsigned long size)
{
char *p=new char[size];//分配失败,不是返回NULL
return p;
}
int main()
{
try
{
char *p = GetMemory(10e11);// 分配失败抛出异常std::bad_alloc
if(!p) //无效
cout<<"failure"<<endl;
delete [] p;
}
catch(const std::bad_alloc &ex)//正确写法
{
cout<<ex.what()<<endl;
}
return 0;
}
nothrow new
在内存分配失败的情况下,该方式不会抛出异常,而是返回NULL。在C++中定义如下:
void * operator new(std::size_t, const std::nothrow_t&) throw();
void operator delete(void*) throw();
实例
#include "stdafx.h"
#include <iostream>
#include <new>
using namespace std;
char *GetMemory(unsigned long size)
{
char *p = new(std::nothrow) char[size];//分配失败,返回NULL
if(NULL == p)
cout << "alloc failure!" << endl;
return p;
}
int main()
{
char *p=GetMemory(10e11);
if(NULL == p)
cout << "failure" << endl;
delete [] p;
return 0;
}
palacement new
palacement意味”放置“,该方式是在一块已经分配的内存之上重新构造对象或对象数组。palacement new不用担心内存是否分配失败,因为其根本不分配内存,而是对已分配的内存再利用。它唯一做的事情就是在已有的空间上初始化对象,调用对象的构造函数。在C++中定义如下:
void* operator new(size_t, void*);
void operator delete(void*, void*);
TIPS:palacement new需要显式调用对象的析构函数,不能使用delete/delete [],因为placement new构造起来的对象或数组大小并不一定等于原来分配的内存大小,使用delete/delete []会造成内存泄漏或者释放内存之后出现运行时错误。
实例
#include "stdafx.h"
#include <iostream>
#include <new>
using namespace std;
class ADT {
int i;
int j;
public:
ADT() {
}
~ADT() {
}
};
int main() {
char *p = new(nothrow) char[sizeof(ADT)+2];
if(p == NULL)
cout << "failure" << endl;
ADT *q = new(p) ADT; //placement new:不必担心内存分配失败
//delete q; //错误!不能在此处调用delete q;
q->ADT::~ADT(); //显式调用析构函数
delete [] p;
return 0;
}
其他
TIPS:
格式:new(类对象指针) 类构造函数([参数])
上述格式可以显示调用类的构造函数。
实例
#include <iostream>
class Test2 {
public:
Test2(int a_, int b_) {
a = a_;
b = b_;
std::cout << "调用构造函数" << std::endl;
}
~Test2() {
std::cout << "调用析构函数" << std::endl;
}
void Print() {
std::cout << a << " " << b << std::endl;
}
int a;
int b;
};
int main()
{
Test2* t2 = new Test2(1, 2);
t2->Print();
new(t2) Test2(2, 2);
t2->Print();
new(t2) Test2(4, 2);
t2->Print();
system("pause");
return 0;
}
注意点
在C++中”有内存空间不一样在内存空间中就有实例对象,而有实例对象,也不一定有内存空间“。
注释:第二部分比如函数的形参(局部变量),在函数调用的时候有实例对象和分配该实例对象的内存空间,但是当函数调用完毕时,形参的内存空间将被回收。
我们知道,在C++中使用malloc申请内存,尤其是对类对象申请内存时,是不会主动调用类的构造函数的,即我们在该情况下应当显式调用类的构造函数。
以下进行测试,我们同样知道,针对包含虚函数的类,在实例化类对象的时候,会生成相应的虚表以及虚表指针,以下进行测试,当我们不显式调用类的构造函数会出现什么情况。
#include <iostream>
class Test2 {
public:
Test2(int a_, int b_) {
a = a_;
b = b_;
std::cout << "调用构造函数" << std::endl;
}
~Test2() {
std::cout << "调用析构函数" << std::endl;
}
virtual void Print() {
std::cout << a << " " << b << std::endl;
}
int a;
int b;
};
int main()
{
Test2* t2 = (Test2*)malloc(sizeof(Test2));
/*new(t2) Test2(2, 2);*/
t2->Print();
//delete t2;
t2->~Test2();
free(t2);
system("pause");
return 0;
}
而当我们显式调用类的构造函数时,情况如下:
int main()
{
Test2* t2 = (Test2*)malloc(sizeof(Test2));
new(t2) Test2(2, 2);
t2->Print();
delete t2;
/*t2->~Test2();
free(t2);*/
system("pause");
return 0;
}
应用
在我们使用C++进行开发项目的过程中,我们可以定义一个全局申请内存的模块,用于管理在项目中所有申请内存的操作,如下:
Common.h
#pragma once
#include <iostream>
#include <map>
#define FIRST_CHOICE_MALLOC(len) FirstChoiceMalloc(len, __FILE__, __LINE__);
void* FirstChoiceMalloc(uint32_t len, const char* file, int32_t line);
void FirstChoiceFree(void* p);
Common.cpp
#include "Common.h"
struct MallocBlockInfo {
uint32_t len;
std::string file;
int line;
};
uint32_t g_blockSize = 0;
std::map<void*, MallocBlockInfo> g_blockMap;
void *FirstChoiceMalloc(uint32_t len, const char * file, int32_t line) {
MallocBlockInfo info;
info.file = file;
info.line = line;
info.len = len;
void* p = malloc(len);
if (NULL == p) {
fprintf(stderr, "Failed to request memory!!!file:%s,line:%d\n",
file, line);
p = NULL;
goto Exit;
}
g_blockMap.insert(std::make_pair(p, info));
g_blockSize++;
Exit:
return p;
}
void FirstChoiceFree(void * p) {
if (NULL == p) {
return;
}
auto iter = g_blockMap.find(p);
if (iter == g_blockMap.end()) {
fprintf(stdout, "FirstChoiceFree: [%p] not find\n", p);
}
else {
g_blockSize--;
g_blockMap.erase(iter);
}
free(p);
p = NULL;
}
示例
class Test{
}
int main() {
Test* test = (Test*)FIRST_CHOICE_MALLOC(sizeof(Test));
new(test) Test();
test->~Test(); //跟malloc同理,free也不会调用类的析构函数,即我们应当显式调用
FirstChoiceFree(test);
test = NULL;
return 0;
}