c++中赋值操作符的重载
来源:互联网 发布:python hmmlearn库 编辑:程序博客网 时间:2024/06/07 16:36
直接抛问题,两个同类型的对象可以相互赋值?
class cls{public: int a; char c; cls() : a(10), c('g') {} cls(int a, char c) : a(a), c(c) {}};int main(void){ cls c1(6, 's'); cls c2; c2 = c1; printf("c2.a = %d, c2.c = %c\n", c2.a, c2.c); return 0;}
编译运行:
显然c++支持两个同类型的对象可以相互赋值的。
”c2 = c1;”注意要跟”cls c2 = c1;”区分开,前者是赋值语句,后者是初始化。对于后者,对象c2会调用类的拷贝构造函数,实现将c1到c2的拷贝。关于拷贝构造函数的使用,在前面http://blog.csdn.net/qq_29344757/article/details/76037255一文中有详细介绍。
两个同类型的对象可以直接使用”=”(赋值操作符)进行赋值,其实这是类已经实现对赋值操作符的重载。但是在上面的代码中,我们并没有定义赋值操作符重载函数,可见,c++类会默认为我们定义。
综合前面所学习的可知,一个空的c++类:
class cls{};
编译器会为我们默认定义构造函数、拷贝构造函数、析构函数以及赋值操作符重载函数,也就变成:
class cls{public: cls(); ~cls(); cls(const cls& c); cls& operator= ();};
那么问题出现,默认的赋值操作符重载函数内部实现的深度拷贝还是浅拷贝?
class cls{public: int a; int *p; cls() { p = new int(0); a = 0; } cls(int a, int b) { p = new int(b); this->a = a; } ~cls() { delete p; }};int main(void){ cls c1(6, 7); cls c2; c2 = c1; //调用默认的赋值运算符重载函数 printf("c2.a = %d, *c2.p = %d\n", c2.a, *c2.p); return 0;}
编译运行:
堆栈出错,提示重复释放内存。
显然,跟默认拷贝构造一个样,默认的赋值运算符重载函数也是浅拷贝,所以指针变量c2.p和c1.p是一样的。我们需要自定义赋值运算符重载函数(顺便把拷贝构造函数也自定义):
class cls{public: int a; int *p; cls() { p = new int(0); a = 0; } cls(int a, int b) { p = new int(b); this->a = a; } //自定义拷贝构造函数 cls(const cls& c) { p = new int(*c.p); } //自定义赋值运算符重载函数 cls& operator= (const cls& c) { if (this == &c) return *this; delete p; p = NULL; p = new int(*c.p); return *this; } ~cls() { delete p; }};int main(void){ cls c1(6, 7); cls c2; c2 = c1; //调用了cls& operator= (const cls& c) printf("c1.p = %p, c2.p = %p\n", c1.p, c2.p); printf("c2.a = %d, *c2.p = %d\n", c2.a, *c2.p); return 0;}
编译运行正常:
下来仔细看看自定义的操作符重载函数:
cls& operator= (const cls& c){ if (this == &c) return *this; delete p; p = NULL; p = new int(*c.p); return *this;}
(1) 参数const cls& c:加上const原因在于我们不希望此函数对用来进行赋值的c做任何修改,其次有加上const的形参,能接受const和非const的实参,反之只能接收非const的实参
(2) 返回值cls&:返回值是返回被赋值着的引用,即*this,这样可以实现连续赋值,即类似于:
x = y = z;
若不是返回引用”cls& “而是直接是”cls”,那返回的是(*this)的副本,再用这个副本做左值,那么就出错了。
(3)避免自赋值:c/c++的语法并不反对类似”a = a”这样的自赋值语法,所以要在操作符重载加以判断避免自赋值操作,一来为了提高效率,二来避免出错。假设如上代码去掉if判断:
cls& operator= (const cls& c){ delete p; p = NULL; p = new int(*c.p); return *this;}
而*this跟参数c是同一个对象,那么在执行”delete p;”后也就意味着c.p也被delete了,那执行到”p = new int(*c.p);”就出错了,因为被delete后的p已经是一个野指针,对一个野指针解引用就会Segmentation fault。
(4)为什么要先”delete p;”再执行new
因为原先的p是通过类的构造函数new的,要再new一个空间并初始化为(*c.p)就需要先将原来p给delete,不然将造成内存泄漏。其实在这里可以复用p原型的堆空间,那么代码将改成:
cls& operator= (const cls& c){ if (this == &c) return *this; *p = *c.p; //这也是深度拷贝 return *this;}
这样改成反而看着简单。
(6)赋值运算符的重载函数只能是类的成员函数,不能是是类的静态函数(因为静态成员函数只能操作类的静态成员),也不能是(友元)全局函数,否则在编译阶段就出错了!假设可以为全局函数,c++类已经默认提供了赋值重载函数了,那么在赋值运算符重载函数(全局函数)和赋值运算符重载函数(类的成员函数)同时存在的情况下,当进行相同类型间的赋值时,编译器就不知道要调用哪一个函数了。再者,假设可以用全局函数重载赋值操作符:
int operator= (int a, cls& c){ //...}int main(void){ cls c(5); 6 = c; //哥们,这就有点过分了 return 0;}
(7) 调用的时机
对比拷贝构造函数和赋值运算符重载函数的代码,可见除了避免自赋值判断之外,赋值运算符重载函数还比拷贝构造函数多了一句delete。一开始我很纳闷,想不出为何,其实那是我忽略了初始化和赋值这两个小玩意。初始化调用的是拷贝构造函数,”p = new int(*c.p);”语句是对象首次动态分配空间中边分配边为该空间初始化的,但是在赋值时调用的是赋值运算符重载函数,”p = newint(*c.p);”是在第二次分配空间的时候变分配边为该空间初始化的,所以需要把上次的new到的空间delete。
- c++中赋值操作符的重载
- C++_指针悬挂和赋值操作符的重载
- C++_指针悬挂和赋值操作符的重载
- C++_指针悬挂和赋值操作符的重载
- 重载赋值操作符的分类
- C++中赋值运算符的重载
- Effective C++(10) 重载赋值操作符时,返回该对象的引用(retrun *this)
- c++ 重载赋值操作符
- C++重载赋值操作符
- C++ 重载赋值操作符
- C++ 操作符赋值重载
- 拷贝构造函数中是否可以调用重载后的赋值运算操作符
- C++:赋值运算符重载
- 拷贝构造函数与重载赋值操作符的区别
- 复制构造函数与赋值操作符的重载
- C++ 重载赋值操作符operator=的高级议题
- 复制构造函数,赋值操作符重载之间的区别
- String 类的拷贝构造函数, 赋值操作符重载, 算数操作符重载
- JAVA企业面试题精选 Web基础 31-40
- python contextlib 上下文管理
- 14.文件系统:高大上的东西——import关键字/模块
- leetcode--Single Number
- IEnumerable和IEnumerator 详解
- c++中赋值操作符的重载
- 动态规划入门-矩阵取数
- leetcode--Single Number II
- java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
- leetcode--Word Break
- Scrapy命令 和 User Agent
- 选择合适的Chrome浏览器安装进行WebGL的开发调试
- 如何使用openssl生成RSA公钥和私钥对
- 晒晒太阳,眼镜就能为手机充电,甚至窗户都成了“发电厂”