一、内存分布
先看C++中程序内存区域划分。
1.栈又称为堆栈,非静态局部变量/函数参数/返回值等;
2.内存映射是高效I/O映射方式,用作进程间通信;
3.堆用于动态内存分配;
4.数据段存储全局数据和静态数据;
5.代码段一般就是可执行代码;
6.内核是用户不能操作的那部分。
二、C++和C语言的动态内存管理
在C语言中我们都知道,C语言的动态内存管理一般用malloc/realloc/calloc 三种增容方式,free一种释放方式,C++中动态内存开辟方式只有一种方式,new和delete这一组,但是他有两种格式。
malloc、realloc和calloc的区别
(1)malloc不能初始化所申请的内存空间,而calloc能够。
(2)realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,新申请的空间的大小如果小于原来的大小,就会导致数据丢失。
(3)realloc申请的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址。当给realloc设置的参数指针若为NULL或者0时就相当于malloc的作用
(4)calloc所申请的空间内容初始化,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零.
class Test
{
public:
class Sum
{
public:
Sum()
{
_count++;
_sum += _count;
}
static int GetSum()
{
return _sum;
}
static void ReSetSum()
{
_count = 0;
_sum = 0;
}
private:
static int _count;
static int _sum;
};
int Sum_solution(int n)
{
Sum::ReSetSum();
Sum* ps = new Sum[n];
delete[] ps;
return Sum::GetSum();
}
};
int Test::Sum::_count = 0;
int Test::Sum::_sum = 0;
int main()
{
Test t;
cout << t.Sum_solution(10) << endl;
system("pause");
return 0;
}
class Test
{
public:
static Test* CreateInstrance(int data)
{
return new Test(data);
}
~Test()
{
cout << "~Test()" << endl;
}
private:
Test(int date)
{
cout << "Test():" << this << endl;
}
Test(const Test& d);
private:
int _date;
};
int main()
{
Test *t = Test::CreateInstrance(10);
delete t;
system("pause");
return 0;
}
一共有这样两种动态内存增容方式,new[]/delete[]内部结构和new/delete是一样的,但是他们的不同在与,new[]是开辟一段连续空间,而new开辟的是单个元素的空间。这两种不能混合使用,必须对应着使用。
三、operator new/operator delete全局函数
注意一点,new/delete只是C++中给出的操作符,并不是函数,new在底层调用operator+new全局函数来申请空间,delete在底层调用operator+delete全局函数来释放空间。
void Test2() {
// 申请单个Test类型的对象
Test* p1 = new Test;
delete p1;
// 申请10个Test类型的对象
Test* p2 = new Test[10];
delete[] p2;
}
/* operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试 执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。 */
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/* operator delete: 该函数最终是通过free来释放空间的 */
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
}
通过上述两个全局函数就可以知道,new也是用malloc来申请空间,不过new是升级过的malloc。
四、定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用,因为内存池分配出的内存是没有初始化的,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class T
{
public:
T()
: _data(0)
{
cout << "Test():" << this << endl;
}
~T()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
void Test()
{
//pt现在指向的只不过是T对象相同大小的一段空间,
//还不能算是一个对象,因为构造函数没有执行
T* pt = (T*)malloc(sizeof(T));
new(pt) T;//如果T类的构造函数没有参数时,此处需要传参。
}
int main()
{
Test();
getchar();
return 0;
}