C++:类中的赋值函数
先来看一个例子:
#include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
};
Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){
//cout<<"调用构造函数1"<<endl;
}
Student(const Student& stu){//拷贝构造函数
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
cout<<"调用拷贝构造函数"<<endl;
}
~Student(){
//cout<<"调用析构函数"<<endl;
}
void show(){
cout<<"Name:"<<Name<<endl;
cout<<"Age:"<<Age<<endl;
cout<<"Gender:"<<Gender<<endl;
}
private:
string Name;
int Age;
string Gender;
}; int main(){
Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
cout<<"---------------------赋值操作之前-----------------"<<endl;
stu2.show();
cout<<"---------------------赋值操作之后-----------------"<<endl;
stu2=stu;
stu2.show();
return ;
}

由上面的例子可以看出,C++支持自定义类型的对象之间的赋值操作,而赋值功能的实现则主要依靠自定义类中的赋值函数。每一个自定义类中都有且只有一个赋值函数,该赋值函数既可以由编译器隐式地定义在自定义类中,也可以有用户通过对赋值运算符=的重载显式地定义在自定义类中:
#include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
};
Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){
//cout<<"调用构造函数1"<<endl;
}
Student(const Student& stu){//拷贝构造函数
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
cout<<"调用拷贝构造函数"<<endl;
}
~Student(){
//cout<<"调用析构函数"<<endl;
}
21 Student& operator=(const Student& stu){ //赋值函数
22 cout<<"调用类中的赋值函数"<<endl;
23 if(this!=&stu){
24 Name=stu.Name;
25 Age=stu.Age;
26 Gender=stu.Gender;
27 }
28 return *this;
29 }
void show(){
cout<<"Name:"<<Name<<endl;
cout<<"Age:"<<Age<<endl;
cout<<"Gender:"<<Gender<<endl;
}
private:
string Name;
int Age;
string Gender;
}; int main(){
Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
cout<<"---------------------赋值操作之前-----------------"<<endl;
stu2.show();
cout<<"---------------------赋值操作之后-----------------"<<endl;
stu2=stu;
stu2.show();
return ;
}

特别注意:
Question 1:类中的赋值函数中的参数为什么加const?
Answer:参数使用cosnt的原因有两个:
• 防止类中的赋值函数对用来赋值的“原对象”进行修改
Student& Student::operator=(Student& stu){
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
4 stu.Name="none";//错误,对用来赋值的“原对象”进行了修改
5 stu.Age=0;//错误
6 stu.Gender="none";//错误
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
•若赋值函数的形参加上const,则赋值函数接受的实参对象既可以是const对象,也可以是非const对象;否则赋值函数能够接受的对象只能是非const对象,而不能是const对象。
Student& Student::operator=(Student& stu){
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
int main(){
const Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
stu2=stu; //错误!不能将const对象赋值给非const对象
return ;
}

Question 2:类中的赋值函数中的参数为什么使用引用?
Answer:避免调用类中的拷贝构造函数在内存中开辟空间来创建形参对象,而是让形参成为实参的别名,从而节省时间和空间,提供编程效率
Student& Student::operator=(const Student &stu){ //参数中使用引用
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
int main(){
const Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
stu2=stu;
return ;
}

Student& Student::operator=(const Student stu){ //参数中没有使用引用
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
int main(){
const Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
stu2=stu;
return ;
}

Question 3:类中的赋值函数的返回值类型为什么是Student&,不可以是Student或void吗?
Answer:在C++中,系统支持变量之间的连续赋值,如:
int a=;
int b,c;
b=c=a;//变量之间的连续赋值,b、c的值均为10
其本质是先将变量a的值赋值给变量c,然后将赋值后的变量c返回到赋值运算符=的右边,再将其赋值给变量b,即:
int a=;
b=(c=a); //即c=a,b=c;
同理,如果类中赋值函数的返回值类型为void,即类中的赋值函数没有返回值,此时编译器将不支持对该类中的对象进行连续赋值
void Student::operator=(const Student &stu){
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
}
int main(){
const Student stu("Tomwenxing",,"male");
Student stu2("Ellen",,"female");
Student stu3;
stu3=stu2=stu; //错误!stu3=stu2=stu相当于stu3.operator=(stu2.operator=(stu)) ,由于赋值函数没有返回值,则该语句无法执行
return ;
}
而赋值函数的返回值类型之所以是Student&而非Student是为了避免函数在返回对象时调用类中的拷贝构造函数在内存中创建临时对象,从而提高程序的运行效率
Student& Student::operator=(const Student& stu){ //返回值类型中使用引用&
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
int main(){
Student stu("Tomwenxing",,"male);
Student stu2;
stu2=stu;
return ;
}

Student Student::operator=(const Student& stu){ //返回值类型中没有使用引用&
cout<<"调用类中的赋值函数"<<endl;
if(this!=&stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}
return *this;
}
int main(){
Student stu("Tomwenxing",,"male");
Student stu2;
stu2=stu;
return ;
}

Question 4:语句“if(this!=&stu)”的作用是什么?
Answer:通过比较赋值者和被赋值者的地址是否相同来判断两者是否是同一个对象,从而避免自赋值(即自己给自己赋值)的情况的发生,原因如下:
• 为了提高程序的运行效率。自己给自己赋值是完全无意义的行为,只会浪费程序的时间资源。
• 当类中的数据成员中含有指针时,自赋值操作可能会带来灾难性的后果。例如假设对象a和b中都含有一个指针ptr,它们分别指向一块通过new动态开辟的内存空间A和内存空间B,当将对象a赋值给对象b时,要求先将对象b中指针ptr指向的内存空间B通过delete释放掉(否则将造成内存泄漏),然后在内存中重新开辟内存空间C以拷贝内存空间A中的内容,并让对象b的指针ptr重新指向内存空间C,从而完成赋值。如果此时允许对象的自赋值,那么对象会在自赋值前先释放自己指针所指的内存空间,然后重新在内存中开辟空间,然而由于之前的内存空间已经释放,此时新内存空间希望拷贝的内容将不复存在,因而造成灾难性后果。
因此,对于类中的赋值函数,一定要先检查是否是自赋值,如果是,直接return *this。
Question 5:自定义类的对象之间的赋值操作的本质是什么?
Answer:本质是调用类中的成员函数(operator=()函数)。如:
#include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
//cout<<"调用默认构造函数"<<endl;
};
Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){
//cout<<"调用构造函数1"<<endl;
}
Student(const Student& stu){//拷贝构造函数
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
cout<<"调用拷贝构造函数"<<endl;
}
~Student(){
//cout<<"调用析构函数"<<endl;
}
21 Student& operator=(const Student& stu){ //赋值函数
22 cout<<"调用类中的赋值函数"<<endl;
23 if(this!=&stu){
24 Name=stu.Name;
25 Age=stu.Age;
26 Gender=stu.Gender;
27 }
28 return *this;
29 }
void show(){
cout<<"\tName:"<<Name<<endl;
cout<<"\tAge:"<<Age<<endl;
cout<<"\tGender:"<<Gender<<endl;
}
private:
string Name;
int Age;
string Gender;
}; int main(){
Student stu("Tomwenxing",,"male");
Student stu2,stu3;
44 stu2=stu;//对stu2进行赋值
cout<<"对象stu2:"<<endl;
stu2.show();
cout<<"------------分界线-------------------"<<endl;
stu3.operator=(stu);//对stu3进行赋值
cout<<"对象stu3:"<<endl;
stu3.show();
return ;
}

C++:类中的赋值函数的更多相关文章
- C++(十六) — 类中引用成员函数、命名空间的使用
1.为什么类中引用成员函数? 类将属性和方法做了封装.类是一种数据类型,也就是:固定大小内存块的别名. 类的定义是一个抽象的概念,定义时不分配内存,当用类定义对象时,才分配一个固定大小的内存块. 此时 ...
- C++空类中的默认函数
定义一个空的C++类,例如 class Empty { } 一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过去就相当于 cla ...
- c++中的赋值函数
在c++中,对于任意一个类Class A,如果程序员不显示的声明和定义上述函数,C++编译器将会自动的为A产生4个public inline 的默认函数,这4个函数最常见的形式为: A() //默认构 ...
- Elisp 中变量赋值函数 set 与 setq 辨析
在 Elisp 中,为变量赋值的函数有 set 与 setq,但是,两者存在很大的差异. 使用 set 赋值: 如果我们想为变量 flowers 赋值为一个 列表 '(rose violet dais ...
- python 类中的某个函数作为装饰器
在python的类中,制作一个装饰器的函数, class A: def wrapper(func): ###装饰器 def wrapped(self,*arg,**kwargs) ... return ...
- vector 类中的 push_back( ) 函数
函数名 push_back,算法语言里面的一个函数名,如: 1) c++中的vector头文件里面就有这个push_back函数: 2) 在vector类中作用为在vector尾部加入一个数据 ...
- 【Java.Regex】使用正则表达式查找一个Java类中的成员函数
代码: import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; imp ...
- 通过反射对任意class类中方法赋值的方式
import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;i ...
- 006.CI4框架CodeIgniter, 加载框架的helper辅助类,调用helper类中的各种函数
01. CI4框架作为一个很成熟的框架,给我们提供了很多helper辅助类,我们在代码中可以很方便的使用,如下所示,我们在Controllers中调用Cookies类的set_cookie函数 < ...
随机推荐
- Using 1.7 requires compiling with Android 4.4 (KitKat); currently using
今天编译一个project,我设置为api 14,可是编译报错: Using 1.7 requires compiling with Android 4.4 (KitKat); currently u ...
- linux定时任务-crontab
使用场景: 有时候线上服务器挂了,或者一些数据推送不正常,一般来说我们需要做的就是将项目重启运行,或者检查核对出问题的位置,来快速解决,很多时候我们不得不登上服务器来查看,这个对于目前工作日益繁忙的我 ...
- 大数据调错系列之:自己总结的myeclipse连接hadoop会出现的问题
在我们学习或者工作中开始hadoop程序的时候,往往会遇到一个问题,我们写好的程序需要打成包放在集群中运行,这无形中在浪费我们的时间,因为程序可以需要不断的调试,然后把最终程序放在集群中即可.为了解决 ...
- IntelliJ IDEA 历史版本下载地址
地址:https://confluence.jetbrains.com/display/IntelliJIDEA/Previous+IntelliJ+IDEA+Releases scala插件:htt ...
- SVG Animation
原文:http://tutorials.jenkov.com/svg/index.html http://tutorials.jenkov.com/svg/svg-animation.html SVG ...
- 深度学习与计算机视觉(11)_基于deep learning的快速图像检索系统
深度学习与计算机视觉(11)_基于deep learning的快速图像检索系统 作者:寒小阳 时间:2016年3月. 出处:http://blog.csdn.net/han_xiaoyang/arti ...
- 【转载】MSXML应用总结 开发篇(上)
原文:http://blog.sina.com.cn/s/blog_48f93b530100ejv9.html 本篇是接前文“MSXML应用总结 概念篇”写的,主要总结一下MSXML DOM接口的应用 ...
- 二维码Aztec简介及其解码实现(zxing-cpp)
Aztec Code是1995年,由Hand HeldProducts公司的Dr. Andrew Longacre设计.它是一种高容量的二维条形码格式.它可以对ASCII和扩展ASCII码进行编码.当 ...
- 使用SDNN (space displacement neural network)进行多字体手写识别
手写单字体的识别,在看过卷积神经网络的mnist例子之后,很容易实现,那么如何实现多字体的同时识别呢? 如下图 LeCun大神所用的是SDNN space displacement neural ne ...
- 2_C语言中的数据类型 (三)原码、反码、补码
1.1 原码 将最高位做为符号位(0代表正,1代表负),其余各位代表数值本身的绝对值 +7的原码是00000111 -7的原码是10000111 +0的原码是00000000 -0的原码是 ...