第一讲 primitives
primitives 指管理内存的 “基础工具”
一、内存的分配和释放
由于 O.S API过于底层,已经与操作系统绑定,可移植性差
该课程研究前四个层次
malloc() 与 free() :分配与释放内存
new 与 delete:
- new (分配内存,调用构造)
- delete(调用析构,释放内存)
::operator new() 与 ::operator delete() :
- ::operator new(),全域中的operator new(), 实现依靠 malloc(), 分配内存
- ::operator delete() ,全域中的operator delete(),实现依靠 free(),释放内存
allocator:分配器
下面将具体介绍这些函数的使用方法。对于不同的编译器,其allocator函数的接口也有所不同:
对于GNU C,不同版本又有所不同:
这张图中的__gnu_cxx::__pool_alloc< T >().allocate()对应于上张图中的alloc::allocate()。这是由于GNU版本的不同
侯捷源码:
#include <iostream>
#include <complex>
#include <memory> //std::allocator
//#include <ext\pool_allocator.h>
//GCC使用,欲使用 std::allocator 以外的 allocator, 就得自行 #include <ext/...>
using namespace std;
namespace jj01
{
void test_primitives()
{
cout << "\ntest_primitives().......... \n";
void* p1 = malloc(512); //512 bytes
free(p1);
complex<int>* p2 = new complex<int>; //one object
delete p2;
void* p3 = ::operator new(512); //512 bytes
::operator delete(p3);
//以下使用 C++ 标准库提供的 allocators。
//其接口虽有标准规格,但实现厂商尚未完全遵守;下面三者形式略异。
#ifdef _MSC_VER
//以下两都是 non-static,定要通過 object 調用。以下分配 3 個 ints.
int* p4 = allocator<int>().allocate(3, (int*)0);
allocator<int>().deallocate(p4, 3);
#endif
#ifdef __BORLANDC__
//以下两函数都是 non-static,定要通过 object 调用。以下分配 5 个 ints.
int* p4 = allocator<int>().allocate(5);
allocator<int>().deallocate(p4, 5);
#endif
#ifdef __GNUC__
//以下两函数都是 static,可通过全名调用之。以下分配 512 bytes.
//void* p4 = alloc::allocate(512);
//alloc::deallocate(p4,512);
//以下两函数都是 non-static,定要通过 object 调用。以下分配 7 个 ints.
void* p4 = allocator<int>().allocate(7);
allocator<int>().deallocate((int*)p4, 7);
//以下两函数都是 non-static,定要通过 object 调用。以下分配 9 个 ints.
void* p5 = __gnu_cxx::__pool_alloc<int>().allocate(9);
__gnu_cxx::__pool_alloc<int>().deallocate((int*)p5, 9);
#endif
}
} //namespace
int main(void)
{
jj01::test_primitives();
return 0;
}
二、基本构件之 new/delete expression
1、new expression
new expression的实质:
-
1、通过调用operator new()分配内存,其中operator new()的内部是调用了malloc()函数
-
2、调用构造方法构造对象
注意,只有编译器可以直接通过pc->Complex::Complex(1, 2)这样的方法调用ctor
我们这样做将产生错误,需要通过placement new 来调用构造函数。
2、delete expression
delete expression 的实质:
-
1、调用析构函数,销毁对象
-
2、调用operator delete()函数释放内存,其中 operator delete()的内部调用了free()函数
测试直接调用 Ctor & Dtor:
#include <iostream>
#include <string>
//#include <memory> //std::allocator
using namespace std;
namespace jj02
{
class A
{
public:
int id;
A() : id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; }
A(int i) : id(i) { cout << "ctor. this=" << this << " id=" << id << endl; }
~A() { cout << "dtor. this=" << this << " id=" << id << endl; }
};
void test_call_ctor_directly()
{
cout << "\ntest_call_ctor_directly().......... \n";
string* pstr = new string;
cout << "str= " << *pstr << endl;
//! pstr->string::string("jjhou");
//[Error] 'class std::basic_string<char>' has no member named 'string'
//! pstr->~string(); //crash -- 其語法語意都是正確的, crash 只因為上一行被 remark 起來嘛.
cout << "str= " << *pstr << endl;
//------------
A* pA = new A(1); //ctor. this=000307A8 id=1
cout << pA->id << endl; //1
pA->A::A(3); //in VC6 : ctor. this=000307A8 id=3
//! pA->A::A(3);
//in GCC : [Error] cannot call constructor 'jj02::A::A' directly
cout << pA->id << endl;
A::A(5); //in VC6 : ctor. this=0013FF60 id=5 dtor. this=0013FF60
//! A::A(5);
//in GCC : [Error] cannot call constructor 'jj02::A::A' directly
// [Note] for a function-style cast, remove the redundant '::A'
cout << pA->id << endl; //in VC6 : 3
//in GCC : 1
delete pA; //dtor. this=000307A8
//simulate new
void* p = ::operator new(sizeof(A));
cout << "p=" << p << endl; //p=000307A8
pA = static_cast<A*>(p);
pA->A::A(2);
//! pA->A::A(2); //in VC6 : ctor. this=000307A8 id=2
//in GCC : [Error] cannot call constructor 'jj02::A::A' directly
cout << pA->id << endl; //in VC6 : 2
//in GCC : 0
//simulate delete
pA->~A(); //dtor. this=000307A8
::operator delete(pA); //free()
}
} //namespace
int main(void)
{
jj02::test_call_ctor_directly();
return 0;
}
编译运行结果如下:
VC下可以直接通过内存空间调用构造函数,但测试在GNU C下无法通过,具体的内容可见代码注解和打印效果。
三、array new
上图主要展示的是关于 array new 内存分配的大致情况。
解释:
在实际new的时候,编译器会分配比你想象更多内存(如下图),先来看看block size 部分,也就是我们实际需要的内存。
当使用new[] 来分配内存,而使用delete来释放内存,对于对象中没有指针类型的数据或者析构函数没有意义,或许没有什么影响;
但对于像标准库中的string一样,string对象中存放着指针指向其他内存空间或者析构函数是有意义的,若使用delete则只会调用一次析构函数,即str[1]会被析构,而str[2]、str[3]的析构函数不会被调用,这里就会导致内存泄漏。
因此,当使用new[] 来分配内存时,需要使用delete[]来释放内存。并且养成良好的编程习惯,new/delete 与 new[]/delete[] 成对出现
在debug模式下:
下面演示array new 与 array delete:
#include <iostream>
#include <new> //placement new
using namespace std;
namespace jj03
{
class A
{
public:
int id;
A() : id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; }
A(int i) : id(i) { cout << "ctor. this=" << this << " id=" << id << endl; }
~A() { cout << "dtor. this=" << this << " id=" << id << endl; }
};
void test_array_new_and_placement_new()
{
cout << "\ntest_placement_new().......... \n";
size_t size = 3;
{
//case 1
//模擬 memory pool 的作法, array new + placement new. 崩潰
A* buf = (A*)(new char[sizeof(A)*size]);
A* tmp = buf;
cout << "buf=" << buf << " tmp=" << tmp << endl;
for (int i = 0; i < size; ++i)
new (tmp++) A(i); //3次 调用ctor 使用了placement new
cout << "buf=" << buf << " tmp=" << tmp << endl;
//! delete [] buf; //crash. why?
//因為這其實是個 char array,看到 delete [] buf; 編譯器會企圖喚起多次 A::~A.
// 但 array memory layout 中找不到與 array 元素個數 (本例 3) 相關的信息,
// -- 整個格局都錯亂 (從我對 VC 的認識而言),於是崩潰。
delete buf; //dtor just one time, ~[0]
cout << "\n\n";
}
{
//case 2
//回頭測試單純的 array new
A* buf = new A[size]; //default ctor 3 次. [0]先於[1]先於[2])
//A必須有 default ctor, 否則 [Error] no matching function for call to 'jj02::A::A()'
A* tmp = buf;
cout << "buf=" << buf << " tmp=" << tmp << endl;
for (int i = 0; i < size; ++i)
new (tmp++) A(i); //3次 ctor 使用了placement new
cout << "buf=" << buf << " tmp=" << tmp << endl;
delete[] buf; //dtor three times (次序逆反, [2]先於[1]先於[0])
}
{
//case 3
//掌握崩潰原因, 再次模擬 memory pool作法, array new + placement new.
//不, 不做了, 因為 memory pool 只是供應 memory, 它並不管 construction,
//也不管 destruction. 它只負責回收 memory.
//所以它是以 void* 或 char* 取得 memory, 釋放 (刪除)的也是 void* or char*.
//不像本例 case 1 釋放 (刪除) 的是 A*.
//
//事實上 memory pool 形式如 jj04::test
}
}
} //namespace
int main(void)
{
jj03::test_array_new_and_placement_new();
return 0;
}
编译运行结果如下:
观察,构造函数与析构函数的调用顺序,
使用new[] 与delete 的情况
使用new[] 与delete[] 的情况
接下来将更具体地展示array new 对象的内存分配情况:
使用new[]分配10个int内存,内存分配如上图所示,首先内存块会有一个头、尾Cookie记录整个内存大小,黄色部分为debug信息,灰色部分才是真正使用到的内存,蓝色部分的12 bytes是为了让该内存块为16字节边界而补充的填充块。在这个例子中delete pi和delete[] pi效果是一样的,因为int没有析构函数。但是下面的例子就不一样了:
Cookie:其中cookie的最后一位用来表示该内存块的状态,
1 表示 已经分配
0 表示 没有分配
在debug模式下,上图通过new[]申请3个Demo空间大小,内存块使用了96 bytes,
计算方法:黄色部分调试信息32 + 4 = 36 bytes;黄色部分下面的“3”用于标记实际分配给对象内存个数,这里是三个所以里面内容为3,占用4 bytes;Demo内有三个int类型成员变量,一个Demo消耗内存3 * 4 = 12 bytes,由于有三个Demo,所以消耗了12 * 3 = 36 bytes空间;到目前为止消耗36 + 4 + 36 = 76 bytes,加上头尾Cookie一共8 bytes一共消耗84 bytes,由于需要16 bytes对齐,所以填充蓝色部分为12 bytes,一共消耗了84 + 12 = 96 bytes。
假设该对象的析构函数有意义,则这里释放内存时需要使用delete[],并且通过内存分配中的标记“3”,编译器将释放三个Demo对象空间,如果不加就会报错。
四、placement new
注意:placement new 根本没有分配 memory,而是直接使用已经分配的内存块,
所以也没有所谓真正的placement delete,只是将与placement new 对应的delete称为placement delete
五、重载
1、C++内存分配的途径
在类中没有重载operator new()时,走的是第二条路线,如果在类中重载了operator new(),那么走的是第一条路线,但最后还是要调用到系统的::operator new()函数,这在后续的例子中会体现。
一般不会在全域中重载 operator new(),operator delete()
2、重载new 和 delete
注意:重载的new 和 delete 必须为 static,因为它的作用是分配内存并构造对象,而此时对象不存在,因此需要为static。
案例中没有为static是因为编译器默认为static,但是最好显示声明为static,助于理解
上面这张图演示了如何在全域中重载 operator new()函数,该方法最后也是模拟了系统的做法,效果和系统的方法一样,但一般不推荐在全域中重载 operator new()函数,因为它对全局有影响,如果使用不当将造成很大的问题。
如果是在类中重载operator new()方法,那么该方法有N多种形式,但必须保证函数参数列表第一个参数是size_t类型变量;对于operator delete(),第一个参数必须是void* 类型,第二个参数size_t是可选项,甚至更多参数
对于operator new[]和operator delete[]函数的重载,和前面类似。
重载placement new
测试案例:
#ifndef __MYFOO__
#define __MYFOO__
#include <iostream>
using namespace std;
class Bad {};
class Foo {
public:
int _id;
long _data;
string _str;
public:
Foo() : _id(0) { cout << "default ctor. this = " << this << " id = " << _id << endl; }
// 模拟在调用operator new分配内存后,调用构造函数时出现异常并抛出,造成内存泄露,调用对应的“placement delete”
Foo(int i) : _id(i) { cout << "ctor. this = " << this << " id = " << _id << endl; throw Bad(); }
virtual ~Foo() { cout << "dtor. this = " << this << " id = " << _id << endl; }
// size_t: unsigned int
// 根据new的作用,分配内存然后构造,所以在对象构造前调用operator new,故使其为static
static void* operator new(size_t size);
static void operator delete(void* p, size_t size);
static void* operator new[](size_t size);
static void operator delete[](void* p, size_t size);
// placement new
// 第一参数必须为size_t
static void* operator new(size_t size, void* start);
static void operator delete(void*,void*);
};
void* Foo::operator new(size_t size)
{
Foo* p = (Foo*)malloc(size);
cout << "Foo::operator new(), size = " << size << " return: " << p << endl;
return p;
}
void Foo::operator delete(void* p, size_t size)
{
cout << "operator delete(), p = " << p << " size = " << size << endl;
free(p);
}
void* Foo::operator new[](size_t size)
{
Foo* p = (Foo*)malloc(size);
cout << "Foo::operator new[](), size = " << size << " return: " << p << endl;
return p;
}
void Foo::operator delete[](void* p, size_t size)
{
cout << "operator delete(), p = " << p << " size = " << size << endl;
free(p);
}
void* Foo::operator new(size_t size, void* start)
{
cout << "Foo::operator new(size_t, void*), size = " << size << " return: " << start << endl;
return start;
}
void Foo::operator delete(void* p, void*)
{
cout << "operator delete(void* p, void*), p = " << p << " size = " << sizeof(p) << endl;
free(p);
}
#endif
void test_new_and_array_new()
{
cout << "test_new_and_array_new()......" << endl;
cout << "sizeof(Foo) = " << sizeof(Foo) << endl;
Foo* p = new Foo();
delete p;
Foo* pArray = ::new Foo[5];
delete[] pArray;
}
void test_placement_new()
{
cout << "test_placement_new()......." << endl;
Foo start;
Foo* p1 = new Foo;
Foo* p2 = new(&start) Foo;
Foo* p3 = new(&start) Foo(1);
Foo* p4 = new Foo(1);
}
int main()
{
test_new_and_array_new();
test_placement_new(); // 测试时,两个函数请单独调用,观察结果
system("pause");
return 0;
}
注意:在Foo的带参构造中,故意抛出异常,来测试placement delete
只有在 调用构造函数时出现异常并抛出,造成内存泄露,才调用对应的“placement delete”
防止,在使用placement new时,由于内存已经分配完成并传入,而构造函数出现异常并抛出,此时造成分配好的内存会被泄漏,因此此时会调用"placement delete"
注意观察测试test_placement_new()
test_new_and_array_new()
test_placement_new()
关于标准库中basic_string的new(extra)扩充申请量
原因:标准库的string多了一组 reference counting
3、allocator
1、per_class_allocator_1
案例测试:
#ifndef __MYSCREEN__
#define __MYSCREEN__
#include <cstddef>
#include <iostream>
using namespace std;
//ref. C++Primer 3/e, p.765
// per-class allocator_1
// 利用内存池的观念【即创建出一大段连续空间的内存,然后将其切割成一小段一小段,物理上不一定连续但逻辑上连续】,减少使用malloc的次数,以此来避免分配cookie所占用不必要的内存
// 该版本缺点:next指针多占用了内存
// 查看版本2(AirPlane.h):使用union解决
class Screen {
public:
Screen(int x) : i(x) { }
int get() { return i; }
static void* operator new(size_t);
static void operator delete(void*);
private:
Screen* next;
static Screen* freeStore;
static const int screenChunk;
private:
int i;
};
Screen* Screen::freeStore = nullptr;
const int Screen::screenChunk = 24;
void* Screen::operator new(size_t size)
{
Screen* p;
if (!freeStore) { // 如果linked list为空,所以申请一大块内存
size_t chunk = screenChunk * size;
freeStore = p =
reinterpret_cast<Screen*>(new char[chunk]);
// 将一大块内存分成片片,当做linked list串联起来
while ( p != &freeStore[screenChunk - 1]) {
p->next = p + 1;
p++;
}
p->next = nullptr;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void Screen::operator delete(void* p)
{
// 将delete object 插回 free list 前端
(static_cast<Screen*>(p))->next = freeStore;
freeStore = static_cast<Screen*>(p);
// 注意:此处delete并没有调用free()函数,
// 说明,只是将内存加入链表管理,并没有归还给操作系统系统
}
#endif
void test_per_class_allocator_1()
{
cout << sizeof(Screen) << endl;
size_t const N = 100;
Screen* p[N];
for (int i = 0; i < N; i++) {
p[i] = new Screen(i);
}
// 输出前10个pointers,比较其间隔
for (int i = 0; i < 10; i++) {
cout << p[i] << endl;
}
for (int i = 0; i < 10; i++) {
delete p[i];
}
}
int main()
{
test_per_class_allocator_1();
system("pause");
return 0;
}
vsCode 编译运行结果如下:
观察结果:每个对象间隔16 bytes
思考:当一块内存使用完后,再次分配一块新的内存,两块内存之间的连接过程。
- 利用内存池的观念【即创建出一大段空间内存,然后将其切割成一小段一小段,物理上不一定连续但逻辑上连续】,减少使用malloc的次数,以此来避免分配cookie所占用不必要的内存
- 该版本缺点:next指针多占用了内存
- 查看版本2(AirPlane.h):使用union解决
2、per_class_allocator_2
案例如下:
#ifndef __MYAIRPLANE__
#define __MYAIRPLANE__
// pre-class allocator_2
// 解决版本1(Screen.h)缺点:通过union关键字来减少使用next而所占耗的内存
// 前两种有共同缺点,
// 就是当第一次分配的内存池使用完后,会再次分配一块大的内存,然后所有内存链接在一起,而不会归还给操作系统
// 并且当多个类时,需要重复的重载operator new
// 查看版本3(allocator.h):将该动作包装起来
class AirPlane {
private:
struct AirPlaneRep {
unsigned long miles;
char type;
};
private:
union {
AirPlaneRep rep;
AirPlane* next; // embedded pointer
};
public:
unsigned long getMiles() { return rep.miles; }
char getType() { return rep.type; }
void set(unsigned long m, char t) {
rep.miles = m;
rep.type = t;
}
public:
static void* operator new(size_t size);
static void operator delete(void* deadObject, size_t size);
private:
static const int BLOCK_SIZE;
static AirPlane* headOfFreeList;
};
AirPlane* AirPlane::headOfFreeList = nullptr;
const int AirPlane::BLOCK_SIZE = 512;
void* AirPlane::operator new(size_t size)
{
if (size != sizeof(AirPlane)) { // 如果由于继承关系传入的size=sizeof(Airplane)大小不等于sizeof(Airplane)时,则调用::operator new!!
return ::operator new(size);
}
AirPlane* p = headOfFreeList;
if (p) { // 已经分配空间,移动链表指针
headOfFreeList = p->next;
} else { // 分配空间已空,分配一大块memory
AirPlane* newBlock = static_cast<AirPlane*>(::operator new(BLOCK_SIZE * sizeof(AirPlane)));
// 将大块内存分成小块内存
for (int i = 0; i < BLOCK_SIZE; i++) {
newBlock[i].next = &newBlock[i + 1];
}
newBlock[BLOCK_SIZE - 1].next = nullptr;
p = newBlock;
headOfFreeList = &newBlock[1];
}
return p;
}
void AirPlane::operator delete(void* deadObject, size_t size)
{
if (deadObject == nullptr) return;
if (size != sizeof(AirPlane)) {
::operator delete(deadObject);
return;
}
AirPlane* carcass = static_cast<AirPlane*>(deadObject);
carcass->next = headOfFreeList;
headOfFreeList = carcass;
// 注意:此处delete同样并没有调用free()函数,
// 说明,只是将内存加入链表管理,并没有归还给操作系统系统
}
#endif
void test_per_class_allocator_2()
{
cout << sizeof(AirPlane) << endl;
size_t const N = 100;
AirPlane* p[N];
for (int i = 0; i < N; i++) {
p[i] = new AirPlane;
}
p[1]->set(1000, 'A');
p[5]->set(5000, 'B');
p[9]->set(9000, 'C');
cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles() << endl;
cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles() << endl;
cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles() << endl;
// 输出前10个pointers,比较其间隔
for (int i = 0; i < 10; i++) {
cout << p[i] << endl;
}
for (int i = 0; i < N; i++) {
delete p[i];
}
}
int main()
{
test_per_class_allocator_2();
system("pause");
return 0;
}
vsCode 编译运行结果如下:
- 解决版本1(Screen.h)缺点:通过union关键字来减少使用next而所占耗的内存
- 前两种有共同缺点,
- 由于没有调用free()函数,当第一次分配的内存池使用完后,会再次分配一块大的内存,然后所有内存链接在一起,分配的内存会越来越多,但是即使这样也没有内存泄露
- 并且当多个类时,需要重复的重载operator new
- 查看版本3(allocator.h):将该动作包装起来
3.static allocator
// allocator.h
#ifndef __MYALLOCATOR__
#define __MYALLOCATOR__
// static allocator
// 将分配特定大小区块的memory allocator包装成一个class allocator
// 这样每个allocator object都是个分配器,体内维护一个freeStore lists
// 案例使用:查看 Foo.h
class allocator_ {
private:
struct obj {
struct obj* next; // embedded pointer 嵌入式指针
};
public:
void* allocate(size_t);
void deallocate(void*);
private:
obj* freeStore = nullptr;
const int CHUNK = 5; // 标准库大小为20
};
void* allocator_::allocate(size_t size)
{
obj* p;
if (!freeStore) {
size_t chunk = CHUNK * size;
freeStore = p = (obj*)malloc(chunk);
for (int i = 0; i < CHUNK; i++) {
p->next = (obj*)((char*)p + size);
p = p->next;
}
p->next = nullptr;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void allocator_::deallocate(void* p)
{
((obj*)p)->next = freeStore;
freeStore = (obj*)p;
}
#endif
// Foo.h
#ifndef __MYFOO__
#define __MYFOO__
#include <cstring>
#include "allocator.h"
class Foo {
public:
long L;
string str;
static allocator_ myAlloc;
public:
Foo(long l) : L(l) {}
static void* operator new(size_t size) {
return myAlloc.allocate(size);
}
static void operator delete(void* pdead) {
return myAlloc.deallocate(pdead);
}
};
allocator_ Foo::myAlloc;
#endif
void test_static_allocator()
{
Foo* p[100];
cout << "sizeof(Foo) = " << sizeof(Foo) << endl;
cout << "sizeof(allocator_) = " << sizeof(allocator_) << endl;
for (int i = 0; i < 23; i++) {
p[i] = new Foo(i);
cout << p[i] << " " << p[i]->L << endl;
}
for (int i = 0; i < 23; i++) { // 比较地址间隔,注意观察每组(CHUNK=5,5个一组)之间的间隔
delete p[i];
}
}
int main()
{
test_static_allocator();
system("pause");
return 0;
}
解决前两种缺点:进行包装,避免重复的动作
将分配特定大小区块的memory allocator包装成一个class allocator
这样每个allocator object都是个分配器,体内维护一个freeStore lists
通过版本的不断迭代,逐渐接近了标准库的写法
4.macro for static allocator
版本四:使用宏定义简化使用
5.标准库的allocator之一
多个链表,对应于多个class
六、new handler
前面提到,在new expression中,重载了operator new ,并且当malloc() == 0 即内存分配失败时,while loop中调用了_callnewh()函数。
当operator new没有能力分配你所申请的memory时,会抛出异常std::bad_alloc exception,或者有些老版本编译器会返回0。
C++编译器会在抛出异常之前(不止一次)调用一个可由client指定的handler,即 new handler(),观察程序员是否对该问题进行解决。
不止一次调用是指,若new handler没有调用abort()或exit(),上图右边的while loop会一直进行,直到获得足够memory
设计良好的new handler()只有两个选择:
- 让更多的memory可用
- 调用abort()或exit()
#include <iostream>
#include <cassert>
using namespace std;
// 此处为一个 new handler
void no_more_memory()
{
cerr << "out of memory" << endl;
abort();
}
int main()
{
// 该函数由标准库提供
// typedef void (*new_handler)();
// new_handler set new_handler(new_handler p) throw(); 传入一个new_handler,返回一个new_handler
// 设计原因,先前有一个new_handler_A,再传入一个new_handler_B,返回以前的new_handler_A以便记录
set_new_handler(no_more_memory);
int* p = new int[10000000000];
assert(p); // 其作用是如果它的条件返回错误,则终止程序执行
system("pause");
return 0;
}
vsCode的测试结果:
七、=default 与 =delete
测试发现:
operator new与operator delete 不能为 =default,
error: ‘static void Foo::operator delete(void*)’ cannot be defaulted
但可以为 =delete,此时的operator new 与 operator delete无法使用
error: use of deleted function 'static void* Foo::operator new(size_t)
最后:
欢迎指正不足或错误的地方。如果文章对你有所帮助,欢迎点赞支持。欢迎转载。