C++动态内存(未完)

C++动态内存(未完)

1.内存中的 静态内存 & 堆heap & 栈stack

静态内存:用来存放局部static对象,类static数据成员以及任何定义在函数之外的变量

栈stack:用来保存定义在函数内的非static对象

堆heap:每个程序拥有一个内存池,这一部分呢叫做自由空间或者堆,这个堆不是数据结构中的堆,其是由链表实现的,程序用堆来动态的分配对象。
注意:堆中变量只能由程序员手动分配和释放,如果不释放,那么可能会由操作系统来释放!

动态内存主要就是说的是堆heap中的内存

PS:动态内存的释放问题被证明是编程中极其容易出错的地方

如果忘记释放了会导致内存泄漏,如果过早释放了,会产生引用非法内存的指针

2.动态内存与智能指针

1. new 与 delete 以及 两种智能指针

C++中的new和Java中的new用途完全不同,C++中的作用如下:

  • new:在动态内存中为对象分配空间并且返回一个指向该对象的指针
  • delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

对于new和delete的基本用法,主要就是new在用于创建类的对象的时候会自动的调用其构造函数来实例化类,而malloc并不会

由于上述说的出现的一系列问题,因此C++11标准库中提供了两种智能指针来管理使用的动态对象:

  • shared_ptr允许多个指针指向一个对象
  • unique_ptr独占所指的对象
  • weak_ptr为伴随类,是一种弱引用,指向shared_ptr所管理的对象

三个都定义在头文件memory中

2.shared_ptr类

1)初始化及支持的操作

shared_ptr同样是一个类模板,和vector类似,

其默认初始化的结果是:创建一个空指针(智能指针中保存一个空指针)

shared_ptr支持的操作(包括unique_ptr):

举个例子:

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

class Node{
private:
    /* data */
    int a, b;
public:
    Node() = default;
    Node(int xc_a, int xc_b) : a(xc_a), b(xc_b) { }
    ~Node();
};

Node::~Node(){ }

int main(){
    shared_ptr<int > p0;
    if(p0) puts("p0 is empty!");
    shared_ptr<Node > p1 = make_shared<Node >(6, 66);

    return 0;
}

说一个make_shared()函数,最安全的分配和使用动态内存的方法就是使用这个函数,这个函数的初始化类似emplace,需要对应的构造函数来初始化对象。

2)shared_ptr的拷贝和赋值 – 引用计数

什么是引用计数呢?可以理解成每个shared_ptr对象都有一个关联的计数器,这个计数器称为引用计数,无论什么时候拷贝一个shared_ptr,计数器都会递减

例如:

  • 用一个shared_ptr初始化另一个shared_ptr时
  • 将其作为参数传递给一个函数
  • 作为函数的返回值

这些都会导致计数器的递减

PS:标准库中的具体实现使用计数器还是某种数据结构,要看具体的实现方式

当计数器递减到0的时候,就会释放这一部分内存

shared_ptr的析构函数就是递减其所指向的对象的计数器

3)shared_ptr导致的空间浪费

shared_ptr在无用的时候,应该保证其被删除,而shared_ptr无用之后仍然保留的一种情况就是:将shared_ptr放入一个容器中,然后重排了容器

切记:如果将shared_ptr放入一个容器中,而后不再需要全部元素,而只是使用其中的一部分,那么一定要用erase删除这些没有用的元素

4)使用了动态生存期的资源的类

程序使用动态内存的原因有以下三种:

  • 程序不知道自己需要使用多少对象
  • 程序不知道所需对象的准确类型
  • 程序需要再多个对象之间共享数据

使用动态内存为了让多个对象能够共享相同的底层数据

由于我们的目的是实现拷贝和其元对象引用相同的底层元素,就一般而言,如果两个对象共享底层数据,那么当某个对象被销毁的时候,不能够单方面的销毁底层数据

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

template<class T>
class Blob { 
private:
    string a, b, c;
public:
    Blob() = default;
    Blob(string xc_a, string xc_b, string xc_c) : a(xc_a), b(xc_b), c(xc_c) { }
};

int main(){
    Blob<string > b1;
    {
        Blob<string > b2 = {"a", "b", "c"};
        b1 = b2;
    }
    // b2 被销毁了, 但是b2中的元素不能够被销毁
    // b1 指向最初由b2创建的元素

    return 0;
}

对于一般的实现来说,无法达到这样的效果,需要使用动态内存来完成这个功能

实现的比较简单,用一个shared_ptr指向vector用于保存共享信息即可

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

class StrBob {
public:
    typedef vector<string >::size_type size_type;
    StrBob();
    StrBob(initializer_list<string > il);
    size_type size() const { return data->size(); }
    void push_back(const string &t) { data->push_back(t); }
    void pop_back();
    string &front();
    string &back();
private:
    shared_ptr<vector<string > > data;
    void check(size_type i, const string &msg) const {
        if(i >= data->size())
            throw out_of_range(msg);
    };
};

inline StrBob::StrBob() : data(make_shared<vector<string > >()) { };
inline StrBob::StrBob(initializer_list<string > il) : data(make_shared<vector<string > >(il)) { }

inline string &StrBob::front(){
    check(0, "front on empty StrBlob");
    return data->front();
}

inline string &StrBob::back(){
    check(0, "back on empty StrBlog");
    return data->back();
}

inline void StrBob::pop_back(){
    check(0, "pop_back on empty StrBlog");
    data->pop_back();
-

int main(){
    StrBob b1;
    {
        StrBob b2 = {"a", "b", "c"};
        b1 = b2;
    }
    cout << b1.front() << endl;

    return 0;
}
// a

— — — 持续更新

0

发表评论

电子邮件地址不会被公开。 必填项已用*标注

20 − 15 =