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函数 < ...
随机推荐
- x$ksppi与x$ksppcv查询隐藏参数
数据库版本:oracle11g 11.0.2.0.4 SQL> desc x$ksppi; Name Null? Type -------------------- -------- --- ...
- SceneKit下关于修改SCNNode 的Shader展示自定义图形
由于某些需求,需要在苹果OS x系统下展示一组点云,准备使用苹果官方的三维显示控件来完成这一功能.场景点云作为离散的点, 如果每个点以SCNnode的形式加入场景中,则回造成过大的内存消耗,笔者电脑下 ...
- maven tomcat plugin 踩坑记
今天在自己家里的电脑上改一个项目,安装的是社区版的 IntelliJ Idea, 没有办法安装 Tomcat 插件来启动项目.尝试用 jettry runer 启动,结果报 java.lang.Inc ...
- C语言学习记录_2019.02.09
打印字符串:%s--------printf("%s","abcd"); 打印字符串数组中某一位:%c--------printf("%c" ...
- 如何保障Go语言基础代码质量?
为什么要谈这个topic? 实践中,质量保障体系的建设,主要针对两个目标: 一是不断提高目标业务测试覆盖率,保障面向客户的产品质量:二就是尽可能的提高人效,增强迭代效率.而构建全链路质量卡点就是整个体 ...
- 【转】Win10/UWP新特性系列—Web
Internet Explorer Internet Explorer 在Windows 10 升级为Edge模式,是一种交互性和兼容性都很强的新型浏览器,该浏览器相比以前的版本更新了超过2000个操 ...
- 20145209刘一阳《JAVA程序设计》课堂测试总结
20145209刘一阳<JAVA程序设计>课堂测试总结 这次重新学习JAVA这门课,我对本学期没有参与的测试进行了总结并制作成二维码方便老师检查,详细内容如下: 第一周课堂测试### ht ...
- 4569: [Scoi2016]萌萌哒
4569: [Scoi2016]萌萌哒 链接 分析: 每次给出的两个区间长度是一样的,对应位置的数字也是一样的,那么可以将两两对应的数字用并查集合并,设最后有$cnt$个不同的集合,答案就是$9\ti ...
- [HNOI2012]永无乡 线段树合并
[HNOI2012]永无乡 LG传送门 线段树合并练手题,写这篇博客只是为了给我的这篇文章找个板子题. 并查集维护连通性,对于不在同一个连通块内的合并操作每次直接合并两颗线段树,复杂度\(O(n \l ...
- java日志框架log4j详细配置及与slf4j使用教程
一.log4j基本用法 首先,配置log4j的jar,maven工程配置以下依赖,非maven工程从maven仓库下载jar添加到“build path” 1 2 3 4 5 <dependen ...