【深蓝学院C++】第8章 动态内存管理 笔记

第8章 动态内存管理

1.动态内存基础

(1)栈内存 VS 堆内存

栈内存的特点:更好的局部性,对象自动销毁

堆内存的特点:运行期动态扩展,需要显式释放

(2)C++ 中通常使用 new 与 delete 来构造、销毁对象

对象的构造分成两步:分配内存与在所分配的内存上构造对象;对象的销毁与之类似

(3)new 的几种常见形式

构造单一对象 / 对象数组
int main()
{
    int * y = new int(2);
    int * y2 = new int[5]{};

    delete[] y2;
}
nothrow new
int main()
{
    int * y = new(std::nothrow) int[5]{};
    if (y == nullptr)    //如果没加std::nothrow会抛出异常,if语句不会被执行
    {
        //...
    }
    std::cout << y[2] << std::endl;
    delete[] y;
}
placement new
int main()
{
    char ch[sizeof(int)];
    int * y = new (ch) int(4);
    std::cout << *y << std::endl;
}
new auto
int main()
{
    int * y = new auto(3);
}

(4)new 与对象对齐

#include 
#inclued <new>
struct alignas(256) Str    //首地址256的整数倍
{

};
int main()
{
    Str * ptr = new Str();
    stdd::cout << ptr << std::endl;
}

(5)delete常见用法

销毁单一对象 / 对象数组

placement delete

(6)使用 new 与 delete 的注意事项

根据分配的是单一对象还是数组,采用相应的方式销毁

delete nullptr

不能 delete 一个非 new 返回的内存

同一块内存不能 delete 多次
int main()
{
    int * ptr = new int(3);
    std::cout << ptr << std::endl;
    delete ptr;
    ptr = nullptr;    //置空之后便可以再次释放
    std::cout << ptr << std::endl;
    delete ptr;
}

2.智能指针

使用 new 与 delete 的问题:内存所有权不清晰,容易产生不销毁,多销毁的情况

(1)C++ 的解决方案:智能指针

auto_ptr ( C++17 删除)

shared_ptr / uniuqe_ptr / weak_ptr

(2)shared_ptr——基于引用计数的共享内存解决方案

1)基本用法
#include 
#include 
#include 

int main()
{
    //int * x = new int(3);    
    std::shared_ptr<int> x(new int(3));
    std::cout << x.use_count() << std::endl;    //引用计数
    std::shared_ptr<int> y = x;
    std::cout << y.use_count() << std::endl;
    std::cout << x.use_count() << std::endl;
}
#include 
#include 
#include 

std::shared_ptr<int> fun()
{
    std::shared_ptr<int> res(new int(100));
    return res;
}

int main()
{
    auto y = fun();
}
 2)reset / get 方法
#include 
#include 
#include 

std::shared_ptr<int> fun()
{
    std::shared_ptr<int> res(new int(100));
    return res;
}

void fun2(int *)
{
    
}

int main()
{
    auto y = fun();
    std::cout << *(y.get()) << std::endl;    //y.get()返回值为int*
    //*(y.get())为int*解引用而不是对智能指针解引用
    fun2(y.get());
}
#include 
#include 
#include 

std::shared_ptr<int> fun()
{
    std::shared_ptr<int> res(new int(100));
    return res;
}

int main()
{
    auto y = fun();
    y.reset(new int(3));
}
3)指定内存回收逻辑

4)std::make_shared(将int(3)与引用计数的内存位置放的尽量近)
#include 
#include 
#include 

int main()
{
    std::shared_ptr<int> ptr(new int(3));
    //std::shared_ptr ptr2 = std::make_shared(3);
    auto ptr2 = std::make_shared<int>(3);
}
4)支持数组( C++17 支持 shared_ptr ; C++20 支持 make_shared 分配数组)
#include 
#include 
#include 

int main()
{
    std::shared_ptr<int[]> ptr(new int[5]);    //C++17
    auto ptr2 = std::make_shared<int[]>(5);    //C++20
}
5)注意: shared_ptr 管理的对象不要调用 delete 销毁
#include 
#include 
#include 

int main()
{
    std::shared_ptr<int> x(new int(5)); 
    //delete x.get();    //程序会崩溃,因为重复释放
    //std::shared_ptr y(x.get());    //引用计数没有传给y,也会重复释放
}

(3)unique_ptr——独占内存的解决方案

1)基本用法
#include 
#include 
#include 

int main()
{
    std::unique_ptr<int> x(new int(5)); 
}
2)unique_ptr 不支持复制,但可以移动
#include 
#include 
#include 

int main()
{
    std::unique_ptr<int> x(new int(5)); 
    std::cout << x.get() << std::endl;
    std::unique_ptr<int> y = std::move(x);
    std::cout << x.get() << std::endl;    
    std::cout << y.get() << std::endl;
}
#include 
#include 
#include 

std::unique_ptr<int> fun()
{
    std::unique_ptr<int> res(new int(5));
    return res;
}
int main()
{
    std::unique_ptr<int> x = fun();
}

(4)weak_ptr——防止循环引用而引入的智能指针

循环引用
#include 
#include 
#include 

struct Str
{
    std::shared_ptr<Str> m_nei;
    
    ~Str()
    {
        std::cout << "~Str() is called!\n";
    }
};

int main()
{
    std::shared_ptr<Str> x(new Str{});    //[x] = 1
    std::shared_ptr<Str> y(new Str{});    //[y] = 1
    x->m_nei = y;    //[y] = 2
    y->m_nei = x;    //[x] = 2
}

(1)基于 shared_ptr 构造

#include 
#include 
#include 

struct Str
{
    std::weak_ptr<Str> m_nei;
    
    ~Str()
    {
        std::cout << "~Str() is called!\n";
    }
};

int main()
{
    //[]表示引用计数
    std::shared_ptr<Str> x(new Str{});    //[x] = 1
    std::shared_ptr<Str> y(new Str{});    //[y] = 1
    x->m_nei = y;    //[y] = 1
    y->m_nei = x;    //[x] = 1
}

(2)lock方法

#include 
#include 
#include 

struct Str
{
    std::weak_ptr<Str> m_nei;
    
    ~Str()
    {
        std::cout << "~Str() is called!\n";
    }
};

int main()
{
    //[]表示引用计数
    std::shared_ptr<Str> x(new Str{});    //[x] = 1
    std::shared_ptr<Str> y(new Str{});    //[y] = 1
    x->m_nei = y;    //[y] = 1
    y->m_nei = x;    //[x] = 1
    
    if (auto ptr = x->m_nei.lock(); ptr)
    {
        std::cout << "Can access pointer\n";
    }
    else
    {
        std::cout << "Cannot access pointer\n";
    }
}

3.动态内存的相关问题

(1)sizeof 不会返回动态分配的内存大小

(2)使用分配器( allocator )来分配内存(推荐)

#include 
#include 
#include 
#include 

int main()
{
    std::allocator<int> al;
    int * ptr = al.allocate(3);    //分配了3个int的内存
    al.deallocate(ptr, 3);         //释放内存
}

(3)使用 malloc / free 来管理内存(C)

(4)使用 aligned_alloc 来分配对齐内存(C)

(5)动态内存与异常安全

(6)C++ 对于垃圾回收的

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