C++内存管理(指针、数组、智能指针)

new和delete

  • new关键字相比于malloc()函数的优点在于,new不仅分配内存,还构造对象,即调用对象相应的构造函数。同样的,相比于free()函数,使用delete关键字时会调用对象的析构函数。
  • new失败时会抛出一个异常,如果没有捕获到这个异常,程序会被终止。可以使用不抛出异常的new版本:
    int* ptr { new(nothrow) int };new失败时会返回nullptr。
  • 使用new关键字时,内存分配在自由存储区中:
    int i { 7 };//变量i存储在栈中。
    int* ptr { new int(8) };//变量ptr存储在栈中,*ptr存储在自由存储区中。
    int** pp { nullptr };//二级指针pp存储在栈中。
    pp = new int* ;//*pp存储在自由存储区中。
    *pp = new int;//**pp存储在自由存储区中。
    

二维数组在栈中和自由存储区中的区别

  • 内存中地址按顺序排列,并没有真正意义上的二维,所以计算机将二维数组以一维数组的方式表示。假设有一个栈上的3x3的二维数组grid:
    int grid[3][3] {};其在内存中的表示如下:
    在这里插入图片描述
  • 自由存储区上的多维数组内存分布是不连续的,假设有一个自由存储区上的3x3的二维数组grid,其在内存中的表示如下:
    在这里插入图片描述
  • 这行代码无法编译成功:int** grid { new int[3][3] };
    在这里插入图片描述
    创建多维数组的正确方式为:
    int** grid { new int*[3] };
    for(int i { 0 };i<3;++i){
    	grid[i] = new int[3];
    }
    

按引用给函数传递长度以知的基于栈的数组(一个晦涩的语法)

  • 此语法只适用于存储在栈中的数组。

    void printArray(int (&array)[3]) {
    	for (int i{ 0 }; i < 3; ++i) {
       		std::cout << array[i] << "\n";
        }
    }
    int main(){
    	int arr[3]{ 1,2,3 };
    	printArray(arr);//此处arr被编译器视为指针。
    }
    
    
  • printArray函数只接收长度为3的数组:
    在这里插入图片描述

  • 可以使用函数模板让编译器自动推断基于栈的数组的大小:

    template<size_t N>
    void printArray(int (&array)[N]) {
    	for (int i{ 0 }; i < N; ++i) {
       		std::cout << array[i] << "\n";
        }
    }
    int main(){
    	int arr[4]{ 1,2,3,5 };
    	printArray(arr);
    }
    

智能指针

unique_ptr

  • unique_ptr拥有资源的唯一所有权,因此无法复制unique_ptr。创建unique_ptr的方式为:
    unique_ptr<People> people { new People() };
    更好的办法是使用make_unique():auto people { make_unique<People>() };
  • unique_ptr重载了解引用运算符*和箭头运算符->,因此可以像使用普通指针那样使用unique_ptr对象。
  • get()方法可以直接获取底层指针。
  • reset()方法可以释放底层指针并使unique_ptr指向另一个指针。
    release()方法可以断开unique_ptr与底层指针的连接,返回资源的底层指针并将unique_ptr设置为nullptr。
  • 这行代码使用unique_ptr保存动态分配的C风格数组:
    auto intArray { make_unique<int[]>(10) };
    能使用数组下标访问元素:intArray[0]=1;
  • 默认情况下,unique_ptr使用标准的new和delete分配和释放内存,可以将其改为自定义的分配和释放函数:
    //自定义分配函数my_malloc
    int* my_malloc(int value) { std::cout << "my_malloc" << std::endl; return new int{ value }; }
    //自定义释放函数my_free,
    void my_free(int* p) { delete p; std::cout << "my_free" << std::endl; }
    int main(){
    	//模板的第二个参数为函数指针 void (*)(int*)
    	std::unique_ptr<int, decltype(&my_free)> intPtr{ my_malloc(5),my_free };
    	std::cout << *intPtr << std::endl;
    }
    

shared_ptr

  • shared_ptr是一个支持共享所有权的智能指针,可以被复制。当多个shared_ptr对象引用同一个资源时,它们通过引用计数来释放资源,只有当最后一个shared_ptr被销毁时,才会释放引用的资源。
  • 使用自定义的释放函数时不需要将函数的类型指定为模板参数。
  • 当有一个shared_ptr实例指向一个原始指针时,第二个shared_ptr只能是第一个shared_ptr的副本,而不能指向原始指针,否则当销毁这两个shared_ptr时会导致双重释放。
    int* array{ new int(8) };//原始指针
    std::shared_ptr<int> shared1{ array ,my_free};//使用自定义的deleter,不需要像unique_ptr那样指定模板类型参数。
    auto shared2{ std::make_shared<decltype(shared1)>(shared1)};//程序正常
    //加上下面这行代码会导致重复释放资源
    std::shared_ptr<int> shared3{ array ,my_free };//又指向了一次原始指针
    
  • shared_ptr支持别名,允许一个shared_ptr与另一个shared_ptr共享一个指针(拥有的指针),但指向不同的对象(存储的指针):
    class Foo {
    public:
    	Foo(int value) :data{ value } {};
    	int data{};
    };
    int main(){
    	auto foo{ std::make_shared<Foo>(42) };
    	//aliasing拥有的指针为foo.get(),存储的指针为&foo->data,只有当foo和aliasing都销毁时,才会释放Foo对象。
    	auto aliasing{ std::shared_ptr<int>{foo,& foo->data} };
    	std::cout << *aliasing << std::endl;//输出42
    }
    

weak_ptr

  • weak_ptr与shared_ptr有关,weak_ptr可以包含shared_ptr管理的资源的引用,但不拥有这个资源。可以使用lock()方法返回关联的shared_ptr。
  • 假如要让对象返回一个指向自己的shared_ptr,以下方法是错误的:
    class Foo{
    public:
    	shared_ptr<Foo> getPointer(){
    		return shared_ptr<Foo>(this);//这行代码会导致双重释放
    	}
    };
    
    正确的方法是使用std::enable_shared_from_this类作为父类,它提供的shared_from_this()方法可以正确地返回shared_ptr,weak_from_this()方法可以正确地返回weak_ptr。
    class Foo:public enable_shared_from_this<Foo>{
    public:
    	shared_ptr<Foo> getPointer(){
    		return shared_from_this();
    	}
    };
    int main(){
    	auto ptr1{ make_shared<Foo>() };
    	auto ptr2{ ptr1->getPointer() };
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平面海螺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值