资料来源:南科大 余仕琪 C/C++ Program Design
LINK:
- CPP/week11 at main · ShiqiYu/CPP · GitHub
- 11.1-some-default-operations_哔哩哔哩_bilibili
- 11.2-an-example-with-dynamic-memory_哔哩哔哩_bilibili
- 11.3-solution1-hard-copy_哔哩哔哩_bilibili
- 11.4-solution2-soft-copy_哔哩哔哩_bilibili
- 11.5-smart-pointers_哔哩哔哩_bilibili
0 概述
本节主要介绍类的动态内存管理。首先,介绍了C++中的一些默认操作;接着介绍了C++中的动态数据分配的示例,而这些C++中的默认操作则会给数据分配留下隐患。最后,介绍了智能指针,虽然智能指针可以实现只管申请不管释放,但是并不能解决内存管理的一切问题,还是需要深刻理解相关内存操作原理。
1 Some Default Operations C++中的一些默认操作
- 默认构造函数
关于默认构造函数,以下两个观点都是误解:
a) 任何类如果没有定义构造函数,则编译器会帮我们合成一个默认构造函数。
b) 合成默认构造函数会对类中的每一个数据成员进行初始化。
只有在编译器需要默认构造函数来完成编译任务的时候,编译器才会为没有任何构造函数的类合成一个默认构造函数,或者是把这些操作插入到已有的构造函数中去。
编译器需要默认构造函数的四种情况,总结起来就是:
a) 调用对象成员或基类的默认构造函数。
b) 为对象初始化虚表指针与虚基类指针。
Ref. 什么是默认构造函数?_默认构造函数是什么意思-CSDN博客
- 默认析构函数
如果没有定义析构函数,系统会帮你定义一个。需要注意,自动定义的析构函数不会做任何操作。
- 默认复制构造函数
在C++中,当一个类没有显式定义拷贝构造函数时,编译器会提供一个默认的拷贝构造函数。这个默认的拷贝构造函数执行的是成员变量之间的浅拷贝,即简单地复制每个成员变量的值到新对象中
- 默认的复制赋值操作符
注意:t2=t1是赋值操作,Mytime t2=t1是构造操作,二者是不同的。和赋值构造函数一样,如果没有定义,系统会自动定义。
C++中这些存在的一些默认操作,虽然快捷方便,但也给后续操作留下隐患。
2 An Example with Dynamic Memory 动态数据分配的示例
- mystring
#pragma once#include <iostream>
#include <cstring>class MyString
{private:int buf_len;char * characters;public:MyString(int buf_len = 64, const char * data = NULL){std::cout << "Constructor(int, char*)" << std::endl;this->buf_len = 0;this->characters = NULL;create(buf_len, data);}~MyString(){delete []this->characters;}bool create(int buf_len, const char * data){this->buf_len = buf_len;if( this->buf_len != 0){this->characters = new char[this->buf_len]{};if(data)strncpy(this->characters, data, this->buf_len);}return true;}friend std::ostream & operator<<(std::ostream & os, const MyString & ms){os << "buf_len = " << ms.buf_len;os << ", characters = " << static_cast<void*>(ms.characters);os << " [" << ms.characters << "]";return os;}
};
- main.cpp
#include <iostream>
#include "mystring.hpp"using namespace std;// Why memory leak and memory double free?
int main()
{MyString str1(10, "Shenzhen");cout << "str1: " << str1 << endl;MyString str2 = str1; cout << "str2: " << str2 << endl;MyString str3;cout << "str3: " << str3 << endl;str3 = str1;cout << "str3: " << str3 << endl;return 0;
}
需要注意的操作
- MyString str1(10, "Shenzhen"); 新建一个str1
- MyString str2=str1 调用的是复制的构造函数 直接构造了一个和str1一样的str2
- str3=str1 是赋值操作 首先构造了str3,接着让str3=str1,使得初始化时便指向了某块分配的区域
存在的问题:
- 所有str最终都指向了同一块地方,当程序结束时候,析构函数会对str1-3释放内存,会多次陨灭同一块区域
- str3初始分配的那块内存由于赋值后被更改指向,使得那块内存被分配但是没使用,并且无法再次指向。
解决方法1:让多个对象指向自己的内存,不再指向同一块内存
- 编写复制构造函数,使其构造时指向新分配的内存
- 编写赋值函数,使其后新建一块内存并赋值,同时销毁原来的内存
- 但是这种方法在创建对象时候需要频繁申请内存,内存的利用率较低
方法2:让共享的内存只释放一次
实现方法:参考OpenCV中的解决方法,但是我没看懂
3 智能指针 Smart Pointers
智能指针可以只管申请,不管释放。
std::unique_ptr 可以实现单一的指向
更多可参考:【C++11】 之 std::unique_ptr 详解_std unique ptr-CSDN博客
如何理解智能指针:智能指针就是一个类模板。因此在指向的对象使用完后就会被销毁。
更多可参考:【C++11】智能指针深度详解(什么是智能指针?为什么需要智能指针?如何使用智能指针?)_智能指针 csdn-CSDN博客