一、主要内容
1、如果所创建的类存在指针成员,一般都需要定义拷贝构造函数、拷贝赋值构造函数与析构函数;
2、new与delete的底层过程;
二、示例代码
代码内容如下:
#ifndef CLASS_H#define CLASS_H#include<iostream>using std::cout;using std::endl;class data_class{public: data_class(double,double); ~data_class(); double Get_data1() { return data1; } void Set_data1(double data1) { data1 = data1; }; void Set_data2(double data2) { data2 = data2; } double Get_data2() { return data2; }private: double data1; double data2;};data_class::data_class(double data1 = 0,double data2=0):data1(data1),data2(data2){}data_class::~data_class(){ cout << "调用data_class析构函数" << endl;}class class_for_copy_test{public: class_for_copy_test(data_class*);//参数初始化; class_for_copy_test(const class_for_copy_test&); class_for_copy_test(const class_for_copy_test*); class_for_copy_test& operator =(const class_for_copy_test&); ~class_for_copy_test();private: data_class* data_point;};class_for_copy_test::class_for_copy_test(data_class* point_new_out =NULL){ data_class* point_new; if(point_new_out ==NULL) point_new = new data_class();//传入为空指针 else point_new = new data_class(point_new_out->Get_data1(),point_new_out->Get_data2());//传入为空指针 data_point = point_new;//传入指针非空}class_for_copy_test::class_for_copy_test(const class_for_copy_test& same_class) { data_class* new_data = new data_class(same_class.data_point->Get_data1(),same_class.data_point->Get_data2()); this->data_point = new_data;}class_for_copy_test::class_for_copy_test(const class_for_copy_test* same_class) { data_class* new_data; if (same_class == NULL) { cout << "传入为空指针" << endl; new_data = new data_class(0, 0); } else new_data = new data_class(same_class->data_point->Get_data1(),same_class->data_point->Get_data2()); this->data_point = new_data;};class_for_copy_test& class_for_copy_test::operator =(const class_for_copy_test& other_object) { if (other_object.data_point == this->data_point) return *this; delete this->data_point; data_class* new_data = new data_class(other_object.data_point->Get_data1(), other_object.data_point->Get_data2()); this->data_point = new_data; return *this;};class_for_copy_test::~class_for_copy_test() { cout << "调用class_for_copy_test析构函数" << endl; delete this->data_point;//析构内部指针}#endif // !CLASS_H
1、包含指针的类的设计
在类class_for_copy_test中定义一个成员变量data_class的指针,data_class类包含两个double类型的成员,其中该函数的拷贝构造函数为:
class_for_copy_test::class_for_copy_test(const class_for_copy_test& same_class) { data_class* new_data = new data_class(same_class.data_point->Get_data1(),same_class.data_point->Get_data2()); this->data_point = new_data;}
由于class_for_copy_test存在默认构造函数class_for_copy_test::class_for_copy_test(data_class* point_new_out =NULL),所以在拷贝构造函数中不用检查same_class是否为NULL,因此直接重新申请一块内存将same_class的值拷贝到新的申请的内存中即可。这样做的目的是防止浅拷贝的产生使得经过拷贝后其成员变量data_point值一样,使得最后在析构的时候两次析构相同的内存区域。
其拷贝赋值构造函数:
class_for_copy_test& class_for_copy_test::operator =(const class_for_copy_test& other_object) { if (other_object.data_point == this->data_point) return *this; delete this->data_point; data_class* new_data = new data_class(other_object.data_point->Get_data1(), other_object.data_point->Get_data2()); this->data_point = new_data; return *this;};
与拷贝构造函数不同的是,拷贝赋值构造函数需要进行以下三步:
1、检查是否为自我拷贝;
2、释放被赋值对象的data_point指针所指空间;
3、开辟新的内存空间并且使用内部data_point指针指向该内存;
需要检测是否为自我赋值的原因是若是自我赋值,则由于先调用了delete this->data_point,因此右值会被释放,那么赋值就失败了。
在析构函数中需要释放程序锁开辟的内存区域:
class_for_copy_test::~class_for_copy_test() { cout << "调用class_for_copy_test析构函数" << endl; delete this->data_point;//析构内部指针}
至此完成了包含指针的类的设计,特别注意的是这个类使用了深拷贝而不能使用默认拷贝构造函数与拷贝赋值构造函数所默认的浅拷贝的设计。
2、new与delete的底层内存变化
在c++中使用new分配的内存位于栈内存中,如果一个对象complex包含两个double的成员对象,那么在内存中使用new complex会分配多少内存呢?new会首先调用一个malloc sizeof(complex)来获知对象需要的内存空间,然后使用对象的构造函数进一步初始化内存,因此就有了后续的placement new。
在32位机器上使用debug模式,IDE为vc中,其内存分布为:
由于一个double所占内存为4字节(32位),分配三个对象需要(8*3)字节的数据内存,在Debuger模式下图中内存的分布顺序为:
1、使用Debuger模式还需要32字节的Debuger头文件;
2、随后的3表示初始化对象数组的个数(便于确定多次调用析构函数的次数等),4个字节;
3、分配对象实际需要的内存数,(8*3)字节;
4、no man land,4个字节,不知道干嘛用的。。。;
5、cookis内存,其主要记录当前内存区块的状态,如图中需要的内存大小为72字节(包含了首尾两个cookis),而使用了内存对齐的话其应该分配16的倍数字节个内存,因此需要分配80字节,80字节使用16进制表示为50H,但是当前的内存是给出去的,所以末尾置1,得到51H,随后将其填充到内存的首尾;
6、填充内存;
在非Debuger模式下消除了Debuger的首部内存,同时也消除了no man land的内存,但是依旧需要cookis与字节对齐。
在进行delete时候就需要注意了,如图所示:
可以看到如果所分配的对象中包含指向其他内存区块的指针,那么实际上应该多次调用析构函数才能够防止内存泄漏,但是如果使用了new[]却使用了delete。
那么对于不包含指正对象的类而言,其实是没有影响的(指的是回收内存方面),因为调用一次析构函数与调用三次的后果是一样的,本身的内存空间在使用delete的free释放后就释放了所有的分配的内存(我使用VS会报错);但是对于包含指针成员的对象而言,仅仅调用了一次析构函数会使得指针成员所指向的除去第一个对象的内存空间全部泄漏,这是一种很危险的做法。