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函数 < ...
随机推荐
- iOS 判断两个颜色是否相同
今天做项目的时候,遇到一个小问题,就是获取UIButton的背景颜色用来和已知颜色做对比,进行点击事件.去查了下文档,有个方法正好可以处理这个问题. bool CGColorEqualToColor ...
- Linux学习笔记(第十章)
vim程序编辑器 vim特点: vim三种模式: 一般模式:打开文档就直接进入编辑模式 -可进行删除,复制等,无法直接编辑文档 编辑模式:按下[i,I,o,O,A,R,r]等字母才会进入编辑模式,按E ...
- 【数据结构与算法】003—排序算法(Python)
写在前面 常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较 ...
- struts2第四天——拦截器和标签库
一.拦截器(interceptor)概述 struts2是个框架,里面封装了很多功能,封装的很多功能都是在拦截器里面. (属性封装.模型驱动等都是封装在拦截器里面) struts2里面封装了很多功能, ...
- 【SHOI2016】黑暗前的幻想乡
题面 题解 如果没有建筑公司的限制,那么就是个\(\mathrm{Matrix\;tree}\)板子 其实有了也一样 发现\(n\leq 17\),考虑容斥 每次钦定一些建筑公司,计算它们包含的边的生 ...
- window下查杀占用端口的进程
一. 查找占用的端口进程号,比如8080 C:> netstat –ano|findstr 8080 C:\Users\chry>netstat -ano | findstr 8080 T ...
- libpcap详解
转发自http://blog.chinaunix.net/uid-21556133-id-120228.html libpcap(Packet Capture Library),即数据包捕获函数库,是 ...
- 洛咕P3250 [HNOI2016]网络 整体二分
这题太神仙了必须写博客... 显然可以想到二分答案.二分一个答案mid,如果所有长度\(\geq mid\)的路径都过x,那么答案一定\(<mid\),否则答案\(\geq mid\). 那么就 ...
- 自制刻度尺插件-前端简易实现"腾讯信用"界面
依据我现有的知识,在前端上"简易"的实现了腾讯信用的界面,同时自己自制了一个竖直的刻度尺插件,曲线的位置可以根据传入的数值动态的改变,这次主要也想总结一下关于jQuery中exte ...
- tomcat7时遇到启动报错问题 SEVERE: ContainerBase.addChild: start:
个人说明:错误日志确实是我的,方法都是从其他地儿借鉴过来的,但肯定是能解决错误的方法,亲身体验,安全保障!!! 还有遇到这种问题,多启动几遍可能就行了. 错误日志: SEVERE: Container ...