赋值运算符、拷贝初始化和this指针
一、赋值运算符和拷贝构造函数(重载技术)
赋值运算符和拷贝构造函数有编译器默认提供,但如果想做更复杂的事,需要重载。
1.下面用一个简单的例子先区分一下赋值运算符和拷贝构造函数:
#include<iostream>using namespace std;class alpha{public: alpha():data(0) {} //没有参数的构造函数 alpha(int d):data(d) {} //一个参数的构造函数 void diplay() //显示数据 { cout<<data<<endl; } alpha(alpha& a) //重载拷贝构造函数 { data=a.data; cout<<"copy constructor invoked! "<<endl; } alpha operator = (alpha& a) //重载赋值运算符 { data=a.data; cout<<"assignment operator invoked! "<<endl; return alpha(data); //单参数的构造函数 }private: int data;};int main(){ alpha a1(32); alpha a2; //无参数构造函数 a2=a1; //赋值运算符 a2.diplay(); alpha a3=a1; //拷贝构造函数 a3.diplay(); alpha a4(a1); //拷贝构造函数 a4.diplay(); return 0;} |

1)首先区别什么是赋值,什么是初始化。
2)程序中重载采用了引用传递,原因:①众所周知,用值传递的参数将在要传递的函数内产生一个副本,没有例外。如果这个对象很大,则副本就会浪费汗多空间。②在某些情况下可能想记录对象的数目。如果编译器在使用赋值运算符时,每次产生一个额外的对象,这样就可能见到比医疗多得多的对象。而引用传递有助于避免创建过多的对象。
3)return alpha(data); 返回值是重载函数所在的对象的一个副本,而不是同一个对象。返回的值可使它将运算符=串联起来:a3=a2=a1;
4) 然而alpha operator = (alpha& a)中值的返回与值的参数一样的缺点:浪费内存空间。但是使用引用来返回函数的值如alpha& operator = (alpha& a) 这样行吗?
回答是不行的:在函数内部创建的非static的局部变量,在函数返回时即被销毁,引用所返回的值,仅仅是实际希望返回值得地址,是没有意义的,甚至会出现严重错误。(在这里我们可以用this指针解决这个问题,后文会解释)
注意:赋值运算符是唯一一个不能继承的运算符。如果在基类重载了赋值运算符,那么在任何派生类中都不能再重载同一函数。
2.看函数的几种情况
1)函数参数
void func(alpha); //以值传递对象func(a1); //函数调用 |
这时拷贝构造函数将会被调用来创建一个对象a1的副本,并将副本交给函数func()操作。(当然引用或者指针就不会调用拷贝构造函数)
2)函数返回值
alpha func() //函数声明a2=func() //函数调用 |
在这里:首先,程序调用拷贝构造函数来穿件一个func()返回值的副本;
然后,这个值再被赋给a2(调用赋值运算符)。
3)拷贝构造函数为alpha(alpha& a) ,为什么不是alpha(alpha a)的形式?
答:拷贝构造函数必须使用引用,否则会报告内存溢出。(因为如果参数用值来传递,就需要创建一个该值的副本。如何创建副本呢?使用拷贝构造函数,但是原函数本来就是要定义拷贝构造函数,这样不断调用自己你说会有什么结果!~)
二、this指针
每一个对象的成员函数都可以访问一种神奇的指针,即指向该对象本身的this指针,因而在任何对象中都可找到所属对象的自身地址。
#include<iostream>using namespace std;class alpha{public: alpha():data(0) {} alpha(int d):data(d) {} void display() { cout<<data<<endl <<this->data<<endl; } //alpha operator = (alpha& a) //重载赋值运算符 //{ // data=a.data; // cout<<"assignment operator invoked! "<<endl; // return alpha(data); //单参数的构造函数 //} alpha& operator = (alpha& a) //重载赋值运算符 { data=a.data; cout<<"assignment operator invoked! "<<endl; return *this; //通过this指针返回值 }private: int data;};int main(){ alpha a1(37); a1.display(); //直接输出和this->data的输出结果是一样的 alpha a2,a3; a3=a2=a1; //调用赋值运算符 cout<<"a2=";a2.display(); cout<<"a3=";a3.display(); return 0;} |
注意实例中的:使用this指针返回值。(从成员函数和重载运算符返回值,this指针是一个更实用的用法)
1)this指针指向的是该成员函数所属的对象,所以*this就是这个对象本身。通常实用引用和this指针从重载赋值运算符返回数据,从而避免创建额外的对象。
2)必须注意:this指针在静态成员函数中是无效的,因为静态成员函数不属于任何特定的对象。
三、dynamic_cast和typeid
这两个功能通常使用在有很多类都由一个基类派生的情况下。为了使动态类型转换能够工作,基类必须是多态的(也就是说至少包含一个虚函数)。
1.dynamic_cast可以改变指针类型
#include<iostream>#include<typeinfo>using namespace std;class Base{public: Base() {} virtual void vertfunc() //要想用dynamic_cast,基类必须是多态类型 {} Base(int b):ba(b){} void show() { cout<<"Base: ba="<<ba<<endl; }protected: int ba;};class derv1:public Base{};class derv2:public Base{public: derv2():Base() {} derv2(int b,int d):Base(b),da(d) {} void show() { cout<<"derv2: ba="<<ba<<" da="<<da<<endl; }private: int da;};bool isDerv1(Base* pUnknown){ derv1* pderv1; if (pderv1=dynamic_cast<derv1*>(pUnknown)) return true; else return false;}int main(){ derv1* d1=new derv1; derv2* d2=new derv2; if (isDerv1(d1)) cout<<"d1是类derv1的一个对象"<<endl; else cout<<"d1不是类derv1的一个对象"<<endl; if (isDerv1(d2)) cout<<"d2是类derv1的一个对象"<<endl; else cout<<"d2不是类derv1的一个对象"<<endl; Base* pbase=new Base(10); derv2* pderv=new derv2(22,33); pbase->show();pbase=dynamic_cast<Base*>(pderv); pbase->show(); //派生类到基类 pbase=new derv2(34,32); pderv->show(); pderv=dynamic_cast<derv2*>(pbase); pderv->show(); //基类到派生类 return 0;} |
2.typeid可以得到未知的对象类型信息
如上面的实例中最后加上:
cout<<typeid(*pbase).name()<<endl; |
运行程序会显示class derv2,因为pbase=new derv2(34,32); 这条语句。
赋值运算符、拷贝初始化和this指针的更多相关文章
- 【原创】c++拷贝初始化和直接初始化的底层区别
说明:如果看不懂的童鞋,可以直接跳到最后看总结,再回头看上文内容,如有不对,请指出~ 环境:visual studio 2013(编译器优化关闭) 源代码 下面的源代码修改自http://blog.c ...
- 默认初始化&拷贝初始化&直接初始化&值初始化&列表初始化
一.各种初始化的形式 /* 定义变量形式一:不指定初始值 */ int a; // 默认初始化 /* 定义变量形式二:指定初始值 */ int b = 1; // 拷贝初始化 int b(1); // ...
- go语言初始化结构体指针
go语言初始化结构体指针 head:=&ListNode{} 或者 head:=new(ListNode)
- C++ 拷贝构造函数、拷贝赋值运算符、析构函数
每一次都会忘,做个笔记吧.想到哪里写到哪里. 拷贝构造函数 第一个参数必须是自身类类型的引用,且任何额外参数都有默认值.(为什么必须是引用?见后解释) 合成拷贝构造函数:如果我们没有为一个类定义拷贝构 ...
- C++ 拷贝控制和资源管理,智能指针的简单实现
C++ 关于拷贝控制和资源管理部分的笔记,并且介绍了部分C++ 智能指针的概念,然后实现了一个基于引用计数的智能指针.关于C++智能指针部分,后面会有专门的研究. 通常,管理类外资源的类必须定义拷贝控 ...
- 拷贝构造函数 & 拷贝赋值运算符
一.拷贝构造函数 1. 形式 class A { public: // ... A(const A &); // 拷贝构造函数 }; 2. 合成拷贝构造函数 编译器总会为我们合成一个拷贝构造函 ...
- [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表
A linked list is given such that each node contains an additional random pointer which could point t ...
- C++中关于指针初始化和使用NULL的理解
1.严禁使用未被初始化的指针:C++创建指针的时候,只分配存储地址的内存,并不会分配存储数据的内存,所以指针可能指向任何位置. (1)使用解除运算符(*)之前,一定要对指针初始化,否则若声明的指针刚好 ...
- (原)C++中指针不初始化就传递的问题
C++中指针的使用.以前在使用指针之前都会初始化.今天没有初始化,然后指针传递后没有内容(testptrnoret),后来发现返回指针的话(testptrret),就可以了. // testptr.c ...
随机推荐
- JavaScript:引用js文件时的编码格式问题
JavaScript:引用js文件时的编码格式问题 如果js文件的编码格式是utf-8,并且含有中文,那么按照正常的方法引用,就会出现乱码的情况. 方法/步骤 如果js文件的编码格式是utf-8, ...
- UnicodeEncodeError
UnicodeEncodeError at /admin/shop/product/add/ 'ascii' codec can't encode characters in position 0-1 ...
- 使用升级助 升级了win10,黑屏,无桌面 解决方案
使用U盘重装即可. 事实证明,win10升级助手实在不咋地 优待又2: (1)保留原win7系统,有后悔药: (2)原系统的软件可用: 缺点: (1)装得慢,一上午: (2)开机慢,三分半 (3)开机 ...
- Android GC 那点事
版权声明:本文由陈昱全原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/170 来源:腾云阁 https://www.qclo ...
- C++语言中cin cin.getline cin.get getline gets getchar 的用法实例
#include <iostream> #include <string> using namespace std; //关于cin cin.getline cin.get g ...
- 悟透javascript读书笔记
1.undefined,null,0,"" 这四个值转换为逻辑值时是false,其他无论简单类型值,对象或者函数转换过来都是true 2.如图 第一个是“声明了一个变量,给变量赋 ...
- c语言数据结构:递归的替代-------回溯算法
1.要理解回溯就必须清楚递归的定义和过程. 递归算法的非递归形式可采用回溯算法.主要考虑的问题在于: 怎样算完整的一轮操作. 执行的操作过程中怎样保存当前的状态以确保以后回溯访问. 怎样返回至上一次未 ...
- 近期十大优秀jQuery插件推荐
当有限的开发知识限制了设计进展,你无法为自己插上创新的翅膀时,jQuery可以扩展你的视野.本文将推荐从jQuery网站的Plugin频道中推选出的近期十款优秀jQuery插件. 1.jQuery U ...
- Qt Charts
简述 Qt Charts模块提供了一套易于使用的图表组件.它采用了Qt Graphics View框架,因此图表可以很容易地集成到现代的用户界面. Qt Charts可以被用作QWidgets.QGr ...
- 如何使用java调用DLL运行C++(初篇)
JNI:Java Native Interface,简称JNI,是Java平台的一部分,可用于让Java和其他语言编写的代码进行交互. 下面是从网上摘取的JNI工作示意图: