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函数 < ...
随机推荐
- asp.net core合并压缩资源文件(转载)
在asp.net core中使用BuildBundlerMinifier合并压缩资源文件 在asp.net mvc中可以使用Bundle来压缩合并css,js 不知道的见:http://www.cnb ...
- Property Injection in Asp.Net Core (转载)
问: I am trying to port an asp.net application to asp.net core. I have property injection (using ninj ...
- iOS 开发:深入理解 Xcode 工程结构(一)转
当我们新建一个 Cocoa 项目时,Xcode 会提供一系列的模板,类似前端的脚手架工具,只需要简单的几个选项,就可以配置好一个项目所需的基本环境. 这些基本环境配置一般包括: 编译选项.证书链选项 ...
- 关于:url、视图函数、模板3者之间参数传递的理解
url获取参数的途径有2个: 1.url中的变量,设置在URL地址中. 2.url中添加字典,设置在URL地址外. urlpatterns = [ path('<year>/<int ...
- 【10.15总结】绕过CSRF的Referer保护
今天下午可能要出远门,所以现在就把总结写好了. Write-up地址:[Critical] Bypass CSRF protection on IBM 这个CSRF漏洞存在于IBM的修改邮箱页面,修改 ...
- spring-data-jpa快速入门(一)——整合阿里Druid
一.概述 官网:https://projects.spring.io/spring-data-jpa/ 1.什么是spring-data-jpa Spring Data JPA, part of th ...
- 不能存在多个@ManyToMany(fetch=FetchType.EAGER)
@LazyCollection(LazyCollectionOption.FALSE) 原地址:https://stackoverflow.com/questions/4334970/hibernat ...
- JavaScript总结(五)
详解DOM(文档对象模型(Docment Object Model)) ✍ DOM中定义了许多节点类型来表示节点的多个方面: 文档节点Document 最顶层的节点(跟节点),代表整个HTML文档, ...
- 查内存命令之free
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL杂记页 回到顶级页面:PostgreSQL索引页 [作者 高健@博客园 luckyjackgao@gmail. ...
- Ubuntu14.04 64位机上安装OpenCV2.4.13(CUDA8.0)版操作步骤
Ubuntu14.04 64位机上安装CUDA8.0的操作步骤可以参考http://blog.csdn.net/fengbingchun/article/details/53840684,这里是在已经 ...