C++智能指针上

一、裸指针

        “裸指针”是最基础的,直接存储内存地址的指针类型。特点:①它本身没有自动的内存管理机制:如它不会自动释放内存,也不会检查是否指向有效的内存区域;②直接操作内存地址,不进行任何的边界检查:如空指针解引用。

int main()
{
    int x = 10;
    int*p = &x;           //裸指针p指向x
    cout << *p << endl;   //输出x的值,10
    return 0;
}

1、动态内存分配:new 与 malloc 详解

        在 C++ 中,动态内存分配是指在程序运行时(而非编译时)从堆(heap)上分配内存。主要有两种方式:new/delete:C++ 的运算符       malloc/free:C 标准库函数。

①new在堆分配的内存是无名的,返回一个指向该对象的指针。

    //分配单个对象
    int* p = new int;       //p指向一个动态分配的、未初始化的无名对象
    int* p2 = new int(42);  //分配并初始化为42
    int* p3 = new int();    //值初始化,*p3=0

    //分配数组
    int* arr = new int[10];//分配10个int的数组

    //释放内存
    delete p;
    delete[] arr;

②malloc返回void*,需要强制类型转换。

    //分配内存
    int* p = (int*)malloc(sizeof(int));          //分配一个int
    int* arr = (int*)malloc(10 * sizeof(int));   //分配10个int的数组

    //释放内存
    free(p);
    free(arr);

③两者区别:

a.基本内存分配与初始化:new支持初始化;malloc不初始化,分配后必须手动赋值,否则读取是未定义的行为。

b.类对象的内存管理:new自动调用构造函数,delete自动调用析构函数;malloc只是分配内存,需要配合placement new使用,且必须手动调用析构函数。

c.数组处理:new[]和delete[]配合使用;malloc需要手动计算数组大小、初始化每个元素。

d.内存分配失败处理:new在失败时抛出异常(std::bad_alloc);malloc在失败时返回NULL/nullptr。

e.与realloc的配合:

//new没有直接对应的realloc功能,需要手动实现:
    int* arrNew = new int[5];
    int* newArrNew = new int[10];
    std::copy(arrNew, arrNew + 5, newArrNew); //将 arrNew 的前5个元素复制到 newArrNew
    delete[] arrNew;
    arrNew = newArrNew;

//malloc可以配合realloc使用
    int* arr = (int*)malloc(5 * sizeof(int));
    // 扩展内存到10个int
    int* newArr = (int*)realloc(arr, 10 * sizeof(int));
    if (newArr)
    {
        arr = newArr;
        arr[8] = 42;  //使用新分配的空间
    }
    free(arr);

2、malloc两种内存分配方式

        ①brk()方式:分配小块内存(通常是小于128KB),通过brk()系统调用会将堆顶指针向更高的内存地址移动,获取内存空间。被free释放时,并不会将内存归还给操作系统,而是将其缓冲在内存池中,供后续分配请求复用。
优点:直接操作堆,内存分配速度较快。缺点:频繁使用,堆内将产生越来越多不可用的碎片,导致“内存泄漏”。
        ②mmap()方式:当用户请求的内存较大时(通常大于128KB),mmap()在进程的虚拟地址空间中(堆和栈的中间,称为文件映射区域的地方)找一块空闲的虚拟内存。free释放时,会把内存归还给操作系统,内存得到真正释放。
缺点:向操作系统申请内存,是通过系统调用的,执行系统调用是要进入内核态的,然后回到用户态,运行态的切换会耗费不少时间。分配的内存释放都会归还给操作系统。所以每次分配的虚拟地址都是缺页状态,在第一次访问该虚拟地址的时候,就会触发缺页中断。

二、智能指针

        “智能指针”是原始指针的封装,解决了裸指针带来的内存管理问题,可以自动管理内存的生命周期。

1、unique_ptr  “独占所有权”

①三种创建方式

#include <iostream>
using namespace std;
#include <memory>

class MyClass {
public:
    MyClass() { cout << "MyClass created!" << endl; }
    MyClass(int x, double y) {}    // 带参数的构造函数
    ~MyClass() { cout << "MyClass destroyed!" << endl; }
};

int main()
{
    //创建unique_ptr的三种方式:
    //1、使用make_unique  "推荐!!!"
    unique_ptr<MyClass> ptr = make_unique<MyClass>();//函数模板必须使用函数调用运算符 () 来调用。
    auto ptr1 = make_unique<MyClass>(42, 3.14); // 正确:传递参数

    //2、使用new运算符
    unique_ptr<MyClass> ptr2(new MyClass());
    //3、使用unique_ptr的构造函数
    MyClass* rawPtr = new MyClass();
    unique_ptr<MyClass> ptr3(rawPtr);

    return 0;
}

②成员函数get()获取原始指针(裸指针)

    MyClass* rawPtr = ptr.get();  //像普通指针一样使用它

注意:get()不改变所有权,unique_ptr仍然拥有资源的所有权,并在销毁时自动释放资源,手动对rawPtr调用delete会导致双重释放,尽量避免滥用get()。

③调用成员函数和解引用

④不可以copy只可以move

不允许复制是为了保证资源管理的唯一性。如果允许就会有多个指针共享相同的资源,导致双重释放等问题。

⑤reset()清空智能指针

reset方法将智能指针指向的对象释放,并将智能指针设置为nullptr。unique_ptr是独占拥有权,因此它会立即释放资源。

⑥作函数参数,值/引用/常量引用传递/右值引用

按值传递:传递所有权。

按引用传递:

常量引用传递时,函数无法修改指针的状态。

右值引用:值传递会触发一次移动构造,右值引用直接绑定到传入的右值,没有额外移动

                  传递所有权。

⑦以值返回:优化为移动,转移所有权。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值