🎐在处理向运输层添加数据报时, 定义了一个结构体, 结构体中包含一个byte
类型的指针data, 用来存储数据存放的地址, 另外还有一个uint32_t
类型的size用来存放数据占用的字节数, 结构体按照8字节对齐, 无论如何调整size和data两个成员的位置, 也都有4个字节无法使用. 这4个字节用不了, 重新定义size的类型为size_t
, 为了方便拼接协议的各个部分, 重载了结构体的+
运算.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 typedef struct Data { size_t size; byte *data; Data () { size = 0 ; data = nullptr ; } ~Data () { printf ("free! \n" ); if (size > 0 && data != nullptr ) { delete [] data; data = nullptr ; size = 0 ; } } struct Data operator +(struct Data & other) { struct Data result; result.size = size + other.size; result.data = new byte[result.size]; memcpy (result.data, data, size); memcpy (result.data + size, other.data, other.size); return result; } } Data;
结构体里面的data存储的是堆中存放数据的地址, 重载+
运算符会生成一个新的对象, 这就涉及到内存管理了, 如果执行a + b + c
这样的运算, 其中相加过程中会生成两个结构体变量, 至少有一个属于临时变量, 运算结束后就会释放, 因此必须在析构函数中释放data
所指向区域的内存. 上面的代码看上去没有问题.
尝试运行下面👇的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Data test (Data & a) { Data b = a; Data c = a + a; Data d = c + a; Data e = b + d; return e; } int main (int argc, const char * argv[]) { Data m; m.size = 3 ; m.data = new byte[3 ]; Data n; n.size = 4 ; n.data = new byte[4 ]; Data l = m + n; Data t = test (l); sleep (10 ); return 0 ; }
程序会报一个错误
1 2 malloc: *** error for object 0x10072d520: pointer being freed was not allocated malloc: *** set a breakpoint in malloc_error_break to debug
大致的意识就是free释放的区域不是malloc申请的, 在这个例子中也就是delete了不属于new申请的区域. 仔细检查了一下代码, 并没有这种情况. 那为什么会出现这种情况呢? 添加断点后发现, 对同一个地址调用了两次delete, 所以自然会报这种错误. 在test
函数中, 临时变量b
离开作用域以后调用了一次析构函数, 这里b被赋值成了a, test
函数中声明的参数是左值引用, test调用前没有拷贝参数到形参, 这里没问题, 问题在于对b复制时, 可能只是浅拷贝, 也就是只对对应的成员变量赋值, 两个data
指向同一个区域. 因此需要重写拷贝函数和拷贝构造函数, 拷贝构造函数一般是变量声明的同时赋值使用, 拷贝函数则是拷贝到已有变量. 拷贝函数般重载运算符=
, 拷贝构造函数有两种常见的形式:
1 2 3 4 5 6 Data b; Data a = b; Data a (b) ;
添加拷贝构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 typedef struct Data { size_t size; byte *data; Data () { size = 0 ; data = nullptr ; } ~Data () { printf ("free! \n" ); if (size > 0 && data != nullptr ) { delete [] data; data = nullptr ; size = 0 ; } } Data (struct Data & other) { if (other.size > 0 && other.data != nullptr ) { size = other.size; data = new byte[size]; memcpy (data, other.data, size); } else { size = 0 ; data = nullptr ; } } struct Data & operator =(struct Data & other) { if (this == &other) { return *this ; } if (other.size != 0 && other.data != nullptr ) { if (size != 0 && data != nullptr ) { delete [] data; } size = other.size; data = new byte[size]; memcpy (data, other.data, size); } return *this ; } struct Data operator +(struct Data & other) { struct Data result; result.size = size + other.size; result.data = new byte[result.size]; memcpy (result.data, data, size); memcpy (result.data + size, other.data, other.size); return result; } } Data;
利用Xcode提供的工具检查一下有没有内存泄漏
打开菜单栏Product
->Profile
即可. 快捷键是⌘
+ I