C++Primer第十二章:动态内存

第十二章:动态内存

一.动态内存与智能指针

在C++中,new和delete分别负责分配和释放空间。

新的标准库提供了两种智能指针,shared_ptr,unique_ptr,还有一种weak_ptr。都定义在memory头文件中。

智能指针也是模板。

最安全的分配和使用内存的方式是调用make_shared函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的一个shared_ptr。

shared_ptr的析构函数会递减它所指向的引用计数,并且在引用计数变为0时销毁对象并释放内存。

如果将shared_ptr存放于一个容器中,而后不再需要全部元素而只使用其中的一部分要记得用erase删除不再需要的那些元素。

默认情况下,使用new动态分配的内存是默认初始化的,也可以使用圆括号花括号进行初始化。

如果我们提供了一个括号包围的值初始化器,我们就可以使用auto来推断我们想要的类型,但是括号中只能有单一初始化器。例如auto ptr = new auto(obj);

一个动态分配的const对象必须初始化(如果有默认构造函数可以隐式初始化)。

如果new不能分配所要求的内存空间,会抛出一个bad_alloc的异常。可以使用定位new的方式改变,所谓定位new就是向new传递一个参数。例如 int *p = new (nothrow) int; 这样请求失败会返回一个空指针。bad_alloc和nothrow都定义在头文件new中。

我们可以用new返回的指针来初始化智能指针,但是这是显式的,我们不能将一个内置指针隐式转换为一个智能指针。

一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象,不过我们也可以将智能指针绑定到一个指向其他类型资源的指针上,不过我们必须提供自己的操作代替delete。

智能指针能够使得在发生异常程序执行顺序发生变化的时候正确释放资源。

使用智能指针的风险根本原因在于同一个原生指针初始化不同的智能指针,这些智能指针之间并无关联。因此使用智能指针有以下注意事项:

  • 不使用相同的内置指针初始化或reset多个智能指针;
  • 不delete get()返回的指针;
  • 不使用get()初始化或reset另一个智能指针;
  • 如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,指针就变为无效了。
  • 如果使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

unique_ptr不支持普通的拷贝或赋值操作。不过有一个例外,就是可以拷贝或赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr。

unique_ptr也可以使用自定义的删除器,不过与shared_ptr不同,具体不同见书。

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,但不改变shared_ptr的引用计数,且不影响shared_ptr指向对象的销毁。weak_ptr用shared_ptr来初始化。

由于对象可能不存在,我们不能直接使用weak_ptr来访问对象,而要使用weak_ptr的lock方法返回一个shared_ptr来访问对象。注意要判断lock返回的为不为空。

二.动态数组

C++提供两种一次分配一个对象数组的方法。一是使用new type [size],一是使用allocator的类,允许我们将分配和初始化分离。

对于new [n],当n=0是合法的,我们不能定义一个长度为0的数组,但是可以动态分配一个长度为0的数组,这个数组返回的指针类似于尾后迭代器。

使用delete[]来释放new[]申请的空间,倒序释放。如果我们在delete一个数组时没加[],或者在delete一个元素指针时加了[],编译器不会报错,但行为未定义。

unique_ptr可以直接管理动态数组,直接用下表运算符可以取值。而shared_ptr不能直接管理动态数组,因为其是用delete来释放内存,所以我们要定义一个delete []的删除器,并且shared_ptr没有定义动态数组的下标运算符。

当我们分配大块内存时可能需要按需构造对象,这个时候就需要用到allocator类了,其为一个模板类,模板为指示可以为类型为T的对象分配内存,其分配和构造、析构和释放是分开的。。

为了使用allocate返回的内存,我们必须用construct构造对象。使用未构造的内存其行为是未定义的。

allocator还有一系列拷贝和填充未初始化内存的算法。

练习

12.1

b2已销毁,b1四个。

12.2

其实就是在string&前面和参数列表后面加上const。答案

12.3

不需要,只有const的StrBlob需要调用常量成员函数,而const的StrBlob不需要添加元素。添加上了反而会有问题,这样就能修改了。

12.4

如果i小于0,因为data->size()返回的是一个无符号数,那么i会转换成一个大于0的数,这样如果data->size()还是大于这个数的话才不会报错,也保证了程序是在data->size()至少大于0的基础上运行的。

12.5

答案

12.6

vector<int>* func1() {
    auto ptr = new vector<int>;
    return ptr;
}

vector<int>* func2(vector<int> *ptr) {
    int temp = 0;
    while (cin >> temp) {
        ptr->push_back(temp);
    }
    return ptr;
}

void func3(vector<int>* ptr) {
    for (auto i : *ptr) {
        cout << i << endl;
    }
    delete ptr;
}

int main() {
    func3(func2(func1()));
    return 0;
}

12.7

shared_ptr<vector<int>> func1() {
    auto ptr = make_shared<vector<int>>();
    return ptr;
}

shared_ptr<vector<int>> func2(shared_ptr<vector<int>> ptr) {
    int temp = 0;
    while (cin >> temp) {
        ptr->push_back(temp);
    }
    return ptr;
}

void func3(shared_ptr<vector<int>> ptr) {
    for (auto i : *ptr) {
        cout << i << endl;
    }
}

int main() {
    func3(func2(func1()));
    return 0;
}

12.8

有错误,p是一个指针,但函数的返回类型是bool,指针的值可以转化为bool,但是原来的指针的值就找不到了,从而释放不了相应的空间。

12.9

第一个r=q表示r和q都指向一个值为42的int,这样赋值原来的r指向的空间就释放不了了。而第二个r2 = q2,r2和q2都是shared_ptr,更改指向原来的自动释放。

12.10

正确的,shared_ptrp制造的对象和原来依然有关联。

12.11

p指向的对象会提前释放。

12.12

(a)合法
(b)不合法,普通指针不能隐式转换为智能指针
(c)不合法,普通指针不能隐式转换为智能指针
(d)合法但有问题,原来的p会成为悬挂指针

12.13

sp指向的对象已经被p释放了,这样当sp自动释放的时候就会有问题。

12.14

答案

12.15

[](connection *p) -> void{disconnect(*p);};

12.16

int main() {
    unique_ptr<int> p1(new int(0));
    unique_ptr<int> p2;
    p2 = p1;
    return 0;
}
test.cpp: In function ‘int main()’:
test.cpp:54:10: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete]’
   54 |     p2 = p1;
      |          ^~
In file included from /usr/include/c++/11/memory:76,
                 from test.cpp:19:
/usr/include/c++/11/bits/unique_ptr.h:469:19: note: declared here
  469 |       unique_ptr& operator=(const unique_ptr&) = delete;

12.17

(a)不合法,unique_ptr需要用指针来初始化
(b)合法
(c)合法
(d)合法
(e)合法
(f)不合法

12.18

shared_ptr是共享的,不需要也不应该单方面的释放。

12.19

答案

12.20

答案

12.21

第一个,可读性更高一点

12.22

答案

12.23

int main() {
    const char* ch1 = "hello ";
    const char* ch2= "world!";
    char* ch = new char [strlen(ch1) + strlen(ch2) + 1];
    strcpy(ch, ch1);
    strcat(ch, ch2);
    cout << ch << endl;

    string str1 = "hello ";
    string str2 = "world!";
    string str = str1 + str2;
    cout << str << endl;

    delete [] ch;
    return 0;
}

12.24

答案

12.25

delete [] pa;

12.26

int main() {
    const char* ch1 = "hello ";
    const char* ch2 = "world!";
    allocator<char> alloc;
    auto ch = alloc.allocate(strlen(ch1) + strlen(ch2) + 1);
    auto begin = ch;
    for (int i = 0; i < strlen(ch1); ++i) {
        alloc.construct(ch++, ch1[i]);
    }
    for (int i = 0; i < strlen(ch2); ++i) {
        alloc.construct(ch++, ch2[i]);
    }
    cout << begin << endl;
    while (ch != begin) {
        alloc.destroy(--ch);
    }
    return 0;
}

12.27-12.33

答案

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