带指针成员类、析构函数出错、复制/赋值构造函数的注意事项
来源:互联网 发布:windows模拟器安卓版 编辑:程序博客网 时间:2024/05/22 13:27
今天下午看面经,有个前辈说 面试被问到了 朋友圈-并查集,然后我发现自己以前没有看并查集,然后就去《王道》上看了看小米的那道朋友圈的面试题,也在网上看了一些大牛的关于并查集的博客。
花了一个多小时大概了解了并查集,然后就编写朋友圈代码,代码编号了,然后发现有人把 这个写成了一个类,并且自己好长时间没有编写类这块的代码了,所以 打算花一点时间写一下。
结果......
悲剧了......
各种问题出现了。
class说明:
class friends{public:friends(int n);//构造函数friends(const friends& f);//复制构造函数friends& operator=(const friends& f);//赋值构造函数~friends();//析构函数int find(int x);void merge(int x, int y);int friendsCircles(int n, int m, int r[][2]);private:int _n;int* _set;};
这个类包括了一个指针变量,所以 需要自己写析构函数、复制构造函数和赋值构造函数(三法则)!
那么需要注意的地方来了:
1、构造函数
friends::friends(int n):_n(n), _set(new int[n+1]){//构造函数,n+1是因为数组的第0个元素没有使用,所以n个元素需要申请n+1个空间cout << "调用构造函数开始!" << endl;int i;for(i = 1; i <= n; ++i)_set[i] = i;cout << "调用构造函数结束!" << endl;}(1)关于n+1。
因为n代表人数,且数组_set中的第0个元素_set[0]没有使用,所以数组大小应为n+1。
这里还应注意:若写成_set(new int[n]),编译没有错误,若不调用析构函数(即不delete _set所指向的空间),也没错误。
但在调用析构函数的情况下,即delete _set所指向的空间时,系统崩溃,并会报错"DAMAGE:after Normal block"!
(2)必须在初始化列表初始化的成员变量(3种):
没有默认构造函数的类类型的成员、const类型的成员变量和引用类型的成员变量。
2、析构函数:
friends::~friends(){//析构函数cout << "调用析构函数开始!" << endl;delete[] _set;cout << "调用析构函数结束!" << endl;}(1)因为_set 指向的 new出来的一个数组,所以需要用delete [] _set,而不是 delete _set。
(2)delete 只能释放堆中的空间,即new出来的空间,若delete 的指针指向 栈的空间,运行会报错!
(3)调用析构函数的几种情况
1)若实例在堆中,即用new创建的实例,eg.
void test1(){//测试在堆上实例化对象int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}};int n = 9;int m = 6;friends *f = new friends(n);//f在堆上int count = f->friendsCircles(n, m, r);cout << "朋友圈的个数:" << count << endl;delete f;//因为f在堆上,所以程序结束时,不会自动调用friends的析构函数,只有用delete时,才调用析构函数}若没有delete f,程序结束时,并不会自动调用析构函数;
此时,只有用delete f 才调用析构函数,这里的delete 是delete operate,是运算符,delete operate 的过程是:先调用 类的析构函数,然后调用operator delete函数,
operate delete函数可以被重载,而delete operate 不可以!同理,new 是delete operate,是运算符,delete operate 的工程是:先调用operate new 函数,然后调用类的构造函数, operate new 可以被重载!
2)若实例在栈中,eg.
void test2(){//测试在栈上实例化对象int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}};int n = 9;int m = 6;friends f(n);//f在栈上int count = f.friendsCircles(n, m, r);cout << "朋友圈的个数:" << count << endl;//因为f在栈上,所以程序结束时,会自动调用friends的析构函数。}
当程序结束了,自动调用析构函数。
关于带指针成员变量的类的书写,大家可以看一下:http://www.cnblogs.com/lucy-lizhi/p/6551308.html
3、复制构造函数
friends::friends(const friends& f){//深复制cout << "调用复制构造函数开始!" << endl;_n = f._n;_set = new int[_n+1];memcpy(_set, f._set, (f._n+1) * sizeof(int));cout << "调用复制构造函数结束!" << endl;}(1)深复制与浅复制
深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。
简单来说:有指针的时候,一定要 深复制!!!
可以参考http://www.cnblogs.com/BlueTzar/articles/1223313.html
(2)调用复制构造函数的情况(有新的对象产生),eg.
void test3(){//测试复制函数 int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}}; int n = 9; int m = 6; friends f(n);//f在栈上 //friends ff(f); friends ff = f; int count = ff.friendsCircles(n, m, r); cout << "朋友圈的个数:" << count << endl;}对象ff是新产生的,所以此时调用复制构造函数,而不是赋值构造函数。
注意:friends ff(f); 与 friends ff = f是等价的!!!
4、赋值构造函数
void test4(){//测试赋值函数int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}};int n = 9;int m = 6;friends f(n);//f在栈上friends ff(n+2);ff = f;int count = ff.friendsCircles(n, m, r);cout << "朋友圈的个数:" << count << endl;}(1)调用赋值构造函数的情况(没有新对象产生),eg.
void test4(){//测试赋值函数int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}};int n = 9;int m = 6;friends f(n);//f在栈上friends ff(n+2);ff = f;int count = ff.friendsCircles(n, m, r);cout << "朋友圈的个数:" << count << endl;}
ff = f , 此时,对象ff不是新产生的,所以调用 赋值构造函数!
朋友圈完整代码见:http://blog.csdn.net/sinat_31135199/article/details/76589295
一下午加一晚上,学习了并查集,复习了带指针成员函数类的书写,复习 构造函数、复制构造函数、赋值构造函数和析构函数,收获还是挺大的!
- 带指针成员类、析构函数出错、复制/赋值构造函数的注意事项
- 带指针成员的类的构造函数,拷贝函数,赋值函数总结
- 写一个类的构造函数、复制构造函数、析构函数、赋值函数
- 字符串类的实现:构造函数、析构函数、复制构造函数和赋值操作符
- 含有指针成员的类的构造函数、拷贝构造函数、赋值运算符
- 类的构造函数、析构函数、成员函数与赋值函数
- 带复制构造函数、赋值运算符的模板队列
- c++的构造函数,析构函数,复制函数,赋值函数,深复制
- 默认构造函数,复制构造函数,赋值操作符函数,析构函数的调用。
- 第十二章-----第十三章 构造函数 复制构造函数 赋值操作符析构函数 static类成员
- 类中的构造函数,析构函数,复制构造函数,赋值函数
- C++ 类的 复制构造函数 和 赋值构造函数
- 类的赋值构造函数和复制构造函数
- String的构造函数,析构函数,复制构造函数,赋值运算符
- 关于默认构造函数、复制构造函数、析构函数、赋值操作的各种测试探索
- 构造函数/复制构造函数/析构函数/赋值操作符的总结
- 带成员函数指针的成员函数
- c++ 使用动态内存分配的类需要显式复制构造函数,赋值构造函数,析构函数
- 微信支付工具类
- 解决VS2013调试ASP.NET中无法调试的问题:当前不会命中断点。在 XXXX.dll 中找到了 XXX.cs 的副本,但是当前源代码与 XXXX.dll 中内置的版本不同。
- 2017.8.2暑假集训第三天
- android studio gradle 离线包 gradle-x.x-all.zip 离线配置
- java操作hbase常用方法总结
- 带指针成员类、析构函数出错、复制/赋值构造函数的注意事项
- jQuery之属性操作、class快捷操作和标签包含内容操作
- nyoj55 懒省事的小明
- 454. 4Sum II(Java)
- Win10系统以管理员身份运行运行框的方法
- linux中的进程查看与管理
- DevOps2-在Jenkins容器中访问Gitlab容器需要特别注意的地方
- 上传项目到github
- 杭电acm—1376 Octal Fractions