【C++初阶5-内存管理】快来学学怎么new一个对象 (doge

前言

博主水平有限,不足之处如能斧正,感激不尽!

本期内容概览:

  • 复习C语言中的动态内存管理
    • malloc
    • free
  • C++中的内存管理
    • new
    • delete
  • C和C++内存管理的区别
  • 定位new表达式
  • 内存泄漏
  • 例题分析

零、数据内存分布

先来分析一下这些基本的内存分布

int globalVar = 1;

static int staticGlobalVar = 1;

void Test()

{

	static int staticVar = 1;

	int localVar = 1;

	int num1[10] = {1, 2, 3, 4};

	char char2[] = "abcd";
	
	char* pChar3 = "abcd";

	int* ptr1 = (int*)malloc(sizeof (int)*4);

	int* ptr2 = (int*)calloc(4, sizeof(int));

	int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);

	free (ptr1);

	free (ptr3);

} 

栈 | 堆 | 数据段(静态区) | 代码段(常量区)

  • globalVar在哪里?
    静态区,全局变量存在静态区

  • staticGlobalVar在哪里?
    静态区,全局static变量存在静态区

  • staticVar在哪里?
    静态区,局部staic变量存在静态区

  • localVar在哪里?
    栈,局部变量存在栈上

  • num1 在哪里?
    栈,局部变量存在栈上

  • char2在哪里?
    栈,char2是局部数组,常量字符串拷贝到栈上

  • *char2在哪里?
    栈,*char2是局部数组的第一个元素,也在栈上

  • pChar3在哪里
    栈,pChar3是局部指针变量

  • *pChar3在哪里?
    常量区,pChar3是指针,指向常量区的常量字符串

  • ptr1在哪里?
    栈,ptr1是局部指针变量

  • *ptr1在哪里?
    堆,ptr指向的空间是动态开辟在堆上的

  • sizeof(num1) = 40

    sizeof(数组名)计算整个数组的大小

  • sizeof(char2) = 5

    sizeof(数组名)计算整个数组的大小,""初始化的字符数组自带’\0’

  • strlen(char2) = 4

    strlen到’\0’停下,计算有效字符个数

  • sizeof(pChar3) = 4/8

    指针变量的大小在32位下是4bytes,64位下是8bytes

  • strlen(pChar3) = 4

    strlen到’\0’停下,计算有效字符个数

  • sizeof(ptr1) = 4

    指针变量的大小在32位下是4bytes,64位下是8bytes

这波夺命连环问,让我回想起那时被C语言指针折磨的恐惧…

一、C语言中的动态内存管理

realloc和calloc在这里不赘述,就讲讲朴实的malloc和free。

malloc

是什么

C语言中用来动态开辟内存的 函数

特性

  • 仅开辟空间
  • 开辟失败返回空指针

怎么用

int main()
{
    int* pi = (int*)malloc(sizeof(int) * 4);
    
    return 0;
}

free

是什么

C语言中用来释放 动态开辟的空间 的函数

特性

  • 仅释放空间

怎么用

int main()
{
    int* pi = (int*)malloc(sizeof(int) * 4);
    free(pi);
    return 0;
}

二、C++中的内存管理

C++和C语言的重要区别:对象!那么C++中的内存管理如果有改变,跟对象脱不开关系。

new 和 delete

new是什么

C++中用来动态开辟空间的 操作符。

new的特性

  • 对内置类型的开辟,和就相当于malloc(语法形式不一样)

    对自定义类型的开辟,开辟后会自动调用其默认构造函数

  • 开辟失败抛异常(异常后面讲)

delete是什么

C++中用来释放动态开辟的空间的 操作符

delete的特性

  • 对内置类型的释放, 相当于free(语法形式不一样)

    对自定义类型的释放,释放前自动调用其析构函数

看了new和delete的特性,就知道他们是“针对对象升级的malloc和free”。

怎么用

  1. 内置类型
int main()
{
    //开辟内置类型
    int* p1 = new int;
    cout << *p1 << endl;
    //释放内置类型
    delete p1;
    
    //开辟内置类型并初始化
    int* p2 = new int(10);
    cout << *p2 << endl;
    //释放内置类型
    delete p2;
    
    
    //开辟内置类型数组
    int* p3 = new int[4];
    for(int i = 0; i<4; i++)
        cout << p3[i];
    cout << endl;
    //释放内置类型数组
    delete[] p3;
    
    //开辟内置类型数组并初始化
    int* p4 = new int[4]{1, 2, 3, 4};
    for(int i = 0; i<4; i++)
        cout << p4[i];
    cout << endl;
    //释放内置类型数组
    delete[] p4;
    
    
    return 0;
}

:0
10
0000
1234
  1. 自定义类型,来看看怎么new一个对象(233
class Date
{
public:
    Date(int y = 2022, int m = 10, int d = 1):_y(y), _m(m), _d(d)
    {
        cout << "Date(int y, int m, int d):_y(y), _m(m), _d(d)" << endl;
    }
    
    ~Date()
    {
        cout << "~Date()" << endl;
    }
    
private:
    int _y = 1;
    int _m = 1;
    int _d = 1;
};

int main()
{
    Date* pd1 = new Date;
    delete pd1;
    
    Date* pd2 = new Date[4];
    delete[] pd2;
  
   	return 0;
}

:Date(int y, int m, int d):_y(y), _m(m), _d(d)
~Date()
Date(int y, int m, int d):_y(y), _m(m), _d(d)
Date(int y, int m, int d):_y(y), _m(m), _d(d)
Date(int y, int m, int d):_y(y), _m(m), _d(d)
Date(int y, int m, int d):_y(y), _m(m), _d(d)
~Date()
~Date()
~Date()
~Date()
  • 一定记住 new 和 delete 对应,new[ ] 和 delete[ ] ]对应
  • 如果new[n]/delete[n],则会调用n次 构造/析构函数

如果混着用,

  • 内置类型不会报错,可以运行
  • 自定义类型不会报错,运行崩溃

底层实现

new的底层调用了一个 operator new( [ ] )

operator new的实现:

/*
operator new:通过malloc申请空间
当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); 
}

只需要抓住这两点:

  • new的底层是malloc实现的
  • new失败了抛异常
Delete的底层调用了一个 operator delete/operator delete[ ]

operator delete的实现:

/* operator delete: 该函数最终是通过free来释放空间的 */
void operator delete(void *pUserData)
{
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
		return; 
}
/* free的实现 */
#define free(p)
_free_dbg(p, _NORMAL_BLOCK)

只需要抓住这一点:

  • delete底层是free实现的

建议

C++中最好不用C的内存函数了,主要原因是自定义类型,如自定义类型容易漏掉初始化等。


三、C和C++内存管理的区别

对比一下malloc和new,free和delete就能知道。

malloc 和 new

malloc只开辟空间 | new开辟完空间对自定义类型会调用其构造函数

malloc是函数,用起来麻烦 | new是操作符,用起来方便

free 和 delete

free只释放空间 | delete释放空间前,对自定义类型会调用其析构函数,而后再释放

*free(nullptr)什么都不做


四、定位new表达式

是什么

定位到已开辟的空间,再次“new”,只不过这个new不真的开辟空间,而是可以对自定义类型调用构造

按这说法,好像没啥用啊,我开辟的时候直接调构造函数来实例化不就好了?

【池化技术】

定位new表达式多用于内存池。什么是内存池?

感性地理解一下:

你住在一个村里,日常用水需要走二里路去水井里打水。久而久之觉得来回跑又麻烦又累,于是接了很长的管道,从家里的院子直通水井,水泵只要看见院子里的蓄水池不满,就自动抽水。如此一来,就实现“用水自由”,再也不用那么麻烦了

放在内存池里看:

频繁的开辟,就要频繁的去申请、找、分配给你。不如自己搞一个内存池,它里边随时有现成的内存可以用,不用老跑去申请。

问题

但这有个问题,构造函数不能在创建对象后显式调用,也就是说,对于已经开辟好的空间,我们无法用它来实例对象了。

定位new就起到作用啦。现在再来回答为什么有定位new表达式

为什么

对已经开辟好的空间,无法显式调用构造函数来实例对象——只能通过定位new调用

特性

  • 不会自动调用析构函数(要手动析构)

怎么用

class A
{
public:
    A(int a):_aa(a)
    {
        cout << "A(int a):_aa(a)" << endl;
    }
    ~A()
    {
        cout << "~A()" << endl;
        _aa = 0;
    }
private:
    int _aa = 0;
};

int main()
{
    A* p = (A*)malloc(sizeof(A) * 4);
    
    //相当于拿已经开辟的空间再“new”一次
    A* paa = new(p)A(10);
    
//    paa->~A();
    
    return 0;
}

:A(int a):_aa(a)

可以看到,并没有自动调用析构


五、内存泄漏

是什么

由于疏忽或错误,未能及时释放 不再使用的内存空间

会如何

程序会逐渐崩溃,只不过是快与慢的差别

怎么避免

  • 事前预防:智能指针
  • 事后查错:内存泄漏检测工具

今天的分享就到这里啦,感谢浏览。

这里是培根的blog,期待与你共同进步!

你可能感兴趣的:(C++,c++,开发语言)