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函数 < ...
随机推荐
- UNIX网络编程 卷2 源代码使用
1. 下载源码,W. Richard Stevens的主页:http://www.kohala.com/start/ wget http://www.kohala.com/start/unpv22e/ ...
- mysql/mariadb学习记录——查询3(AVG、SUM、COUNT)函数
AVG() 求平均数函数: //求emp表中的sal属性的平均值 mysql> select avg(sal) as salAverage from emp; +-------------+ | ...
- 时间序列深度学习:状态 LSTM 模型预测太阳黑子
目录 时间序列深度学习:状态 LSTM 模型预测太阳黑子 教程概览 商业应用 长短期记忆(LSTM)模型 太阳黑子数据集 构建 LSTM 模型预测太阳黑子 1 若干相关包 2 数据 3 探索性数据分析 ...
- leetcode之转置矩阵
转置矩阵 题目描述: 给定一个矩阵 A, 返回 A 的转置矩阵. 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引. 示例 1: 输入:[[1,2,3],[4,5,6],[7,8,9]] ...
- spring-data-jpa快速入门(一)——整合阿里Druid
一.概述 官网:https://projects.spring.io/spring-data-jpa/ 1.什么是spring-data-jpa Spring Data JPA, part of th ...
- c++ 文件操作 重新命名 删除
教学内容: l 文件重命名rename l 文件删除remove 文件重命名rename int rename( const char *oldname, const char *newname ...
- luogu1117 [NOI2016]优秀的拆分
luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...
- cogs1885 [WC2006]水管局长数据加强版
BZOJ卡不过灰常蛋疼(毕竟人蠢自带巨大常数 这和动态维护最小生成树很像,但加边变成了删边,似乎没法做了. 然后根据之前的套路离线做,删边变成加边,就可以做了orz 二分查找的:(慢 // It is ...
- 在Javascript中 声明时用"var"与不用"var"的区别,== 和 ===的区别
今天,被问到两个JS问题,当时没回答到重点,问题虽然看起来简单,但是细节却马虎不得,在此做下记录: 1. 在Javascript中 声明时用"var"与不用"var&qu ...
- JMeter下Groovy和BeanShell语言在不同组件中性能差异实践探究
一般而言JMeter下性能最好的是jar包这类java原生请求,对于JMeter并没有原生支持的请求,一般都会将其直接编译为jar包,然后再JMeter中调用,这样性能最好. 但是有些需求并不适合用j ...