C++编程语言类对象的赋值与复制介绍(二)
本系列文章主要介绍 C++ 编程语言中类对象的赋值操作、复制操作,以及两者之间的区别,另外还会介绍“深拷贝”与“浅拷贝”的相关知识。
本文为系列文章的第二篇,主要介绍 C++ 编程语言中类对象的复制的相关知识。
1 对象的复制
1.1 What
相对于“对已声明的对象使用赋值运算符进行的对象赋值”操作,使用拷贝构造函数操作对象的方式,称为“对象的复制”。
类的拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。拷贝构造函数的作用为:在创建一个新对象时,使用一个已经存在的对象去初始化这个新对象。例如语句“ClassA obj2(obj1);”就使用了拷贝构造函数,该语句在创建新对象 obj2 时,利用已经存在的对象 obj1 去初始化对象 obj2。
1.2 拷贝构造函数的特点
拷贝构造函数有以下特点:
- 拷贝构造函数也是一种构造函数,所以其函数名与类名相同,并且该函数也没有返回值类型;
- 拷贝构造函数只有一个参数,并且该参数是其所属类对象的引用;
- 每一个类都必须有一个拷贝构造函数,可以根据需要重载默认的拷贝构造函数(自定义拷贝构造函数),如果没有重载默认的拷贝构造函数,系统就会生成产生一个默认的拷贝构造函数,默认的拷贝构造函数将会复制出一个数据成员完全相同的新对象;
1.3 自定义拷贝构造函数
这里展示一个自定义拷贝构造函数的示例代码(object_assign_and_copy_test2.cpp),内容如下:
#include <iostream>using namespace std;class ClassA
{
public:// 普通构造函数ClassA(int i, int j){m_nValue1 = i;m_nValue2 = j;}// 自定义的拷贝构造函数ClassA(const ClassA& obj){m_nValue1 = obj.m_nValue1 * 2;m_nValue2 = obj.m_nValue2 * 2;}// 打印成员变量的值void ShowValue(){cout << "m_nValue1 is: " << m_nValue1 << ", m_nValue2 is: " << m_nValue2 << endl;}
private:int m_nValue1;int m_nValue2;
};int main()
{// 创建并初始化对象obj1,此处调用了普通构造函数ClassA obj1(1, 2);// 创建并初始化对象obj2,此处调用了自定义的拷贝构造函数ClassA obj2(obj1);obj1.ShowValue();obj2.ShowValue();return 0;
}
编译并执行上述代码,结果如下:
上述执行结果表明,通过调用自定义的拷贝构造函数,在创建对象 obj2 时,根据对象 obj1 的成员变量的值,完成了自定义的初始化过程。
1.4 调用形式上的区别
可以从调用内容上,对“对象的赋值”和“对象的复制”进行区分。两者的调用内容的对应关系如下:
- 对象的赋值:指的是调用了类的赋值运算符,进行的对象的拷贝操作;
- 对象的复制:指的是调用了类的拷贝构造函数,进行的对象的拷贝操作。
上面的对应关系是不严谨的,因为有些情况下,即使使用了赋值运算符“=”,但其实最终使用的仍然是类的拷贝构造函数,这就引出了拷贝构造函数的两种调用形式。
拷贝构造函数的调用语法分为以下两种:
- 类名 对象2(对象1)。例如:“ClassA obj2(obj1);”,这种调用拷贝构造函数的方法称为“代入法”;
- 类名 对象2 = 对象1。例如:“ClassA obj2 = obj1;”,这种调用拷贝构造函数的方法称为“赋值法”。
拷贝构造函数的“赋值法”就很容易与“对象的赋值”场景混淆,二者之间的区别是:对象的赋值场景必须是建立在源对象与目标对象均已声明的基础上;而拷贝构造函数函数的赋值法,必须是针对新创建对象的场景。
下面给出简单的示例代码说明两者的区别。
【对象的赋值】:
// 声明对象obj1和obj2ClassA obj1;ClassA obj2;obj1.SetValue(1, 2);// 对象赋值场景 —— 将obj1的值赋给obj2obj2 = obj1;
【拷贝构造函数的“赋值法”】:
// 创建并初始化对象obj1,此处调用了普通构造函数ClassA obj1(1, 2);// 创建并初始化对象obj2,此处调用了自定义的拷贝构造函数ClassA obj2 = obj1;
当然,为了代码含义的清晰化及代码的可读性,建议使用拷贝构造函数的“代入法”,更可以让人一眼就看出调用的是拷贝构造函数。
1.5 调用拷贝构造函数的三个场景
1.5.1 类对象初始化
当使用类的一个对象去初始化另一个对象时,会调用拷贝构造函数(包括“代入法”和“赋值法”)。示例代码内容如下:
// 创建并初始化对象obj1,此处调用了普通构造函数ClassA obj1(1, 2);// 创建并初始化对象obj2,此处调用了自定义的拷贝构造函数ClassA obj2(obj1); // 代入法ClassA obj3 = obj1; // 赋值法
1.5.2 类对象作为函数参数
当类对象作为函数形参时,在调用函数进行形参和实参转换时,会调用拷贝构造函数。
示例代码内容如下:
// 形参是类ClassA的对象obj
void funA(ClassA obj)
{obj.ShowValue();
}int main()
{ClassA obj1(1, 2);// 调用函数funA时,实参obj1是类ClassA的对象// 这里会调用拷贝构造函数,使用实参obj1初始化形参对象objfunA(obj1);return 0;
}
说明:在上面的 main 函数内,语句“funA(obj1);”就会调用拷贝构造函数。
1.5.3 类对象作为函数返回值
当函数的返回值是类的对象时,在函数调用完毕将返回值(对象)带回函数调用处,此时会调用拷贝构造函数,将函数返回的对象赋值给一个临时对象,并传到函数的调用处。
示例代码内容如下:
// 函数funB()的返回值类型是ClassA类类型
ClassA funB()
{ClassA obj1(1, 2);// 函数的返回值是ClassA类的对象return obj1;
}int main()
{// 定义类ClassA的对象obj2ClassA obj2;// funB()函数执行完成、返回调用处时,会调用拷贝构造函数// 使用obj1初始化obj2obj2 = funB();return 0;
}
说明:在上面的 main 函数内,语句“obj2 = funB();”就会调用拷贝构造函数。由于对象 obj1 是在函数 funB 中定义的,当函数 funB 结束时,obj1 的生命周期就结束了,因此在函数 funB 结束之前,执行语句"return obj1"时,会调用拷贝构造函数将 obj1 的值拷贝到一个临时对象中,这个临时对象是系统在主程序中临时创建的。funB 函数结束时,对象 obj1 消失,但是临时对象将会通过语句“obj2 = funB()”赋值给对象 obj2,执行完这条语句后,临时对象也自动消失了。
关于“深拷贝”与“浅拷贝”,以及赋值运算符的重载、拷贝构造函数的重载的相关内容,请点击此处。
C++编程语言类对象的赋值与复制介绍(二)相关推荐
- 对象的赋值和复制(转)
一.对象的赋值和复制 1.对象的赋值 如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的值可以赋给另一个同类的对象.这里所指的对象的值是指对象中所有数据成员的值. ...
- C++对象的赋值和复制
C++对象的赋值 1.1对象之间的赋值是用"="运算符来实现的,"="在c++中扩展为重载运算符来实现对象间的赋值. t1=t2; 1.2对象赋值是对数据成员的 ...
- java 反射创建对象并赋值_[原创] Java JDBC连接数据库,反射创建实体类对象并赋值数据库行记录(支持存储过程)...
1 import java.lang.reflect.*;2 import java.sql.*;3 import java.util.*;4 5 public classSqlHelper {6 / ...
- C++派生类对象和基类对象赋值
在C++中,我们允许 将派生类对象赋给基类对象.(不允许将基类对象赋给派生类对象) 只会将基类对象成员赋值 用基类指针指向派生类对象.(不允许用派生类指针指向基类对象) 基类指针只能操作基类中的成员 ...
- C++:类对象的复制和赋值
//例1.类对象复制,拷贝构造函数#include<iostream> using namespace std;class Student { public:Student(){cout ...
- (一二四)给类对象赋值、以及类对象的返回值
于直接给对象赋值: 之前学过,如何给对象在初始化时进行赋值. 对于C++11来说,初始化方式有三种: ① man c = man{ "cc",1 }; ② man d = { &q ...
- java类向拦截器传值_MyBatis拦截器:给参数对象属性赋值的实例
该拦截器的作用:在进行增加.修改等操作时,给数据模型的一些通用操作属性(如:创建人.创建时间.修改人.修改时间等)自动赋值. 该实现是在dao层拦截,即存入db前最后一层.后经分析,不是很合理,改为在 ...
- 类选择器遍历赋值_利用反射实现配置表数据到类对象数据的转换
在游戏开发中,配置表是不可少的.通常我们将一个类,做成一个配置表,将配置表每列的索引都和类的字段名严格对应起来. 先实例化一个类的对象,然后通过反射来遍历类中的字段,通过field.SetValue( ...
- c++,派生类对象可以对基类赋值,基类对派生类不可以赋值
派生类对象可以对基类对象赋值,赋值时属于派生类独有的部分就舍弃不用. #include <iostream> using namespace std;class DemoA { publi ...
- python类对象赋值_Python对象赋值、浅拷贝、深拷贝
Python中,基本数据类型,理解为常见数据类型:布尔型.整型.浮点型.字符串.列表.元组.字典.集合,随语言不同而不同,但是根据在内存中存储方式的不同,区分开原子类型和容器类型. 对象赋值 对象的赋 ...
最新文章
- 关于共用体所占的内存空间的问题
- 国防科技大学教授:殷建平——计算机科学理论的过去、现在与未来
- 用symbol来获得ShadowSSDT的原始地址和函数名
- java图形化界面设置焦点_如何在更新窗口时防止Java图形程序窃取焦点?
- aop 获取方法入参出参_ASM字节码编程 | JavaAgent+ASM字节码插桩采集方法名称及入参和出参结果并记录方法耗时...
- var_dump() 与 print_r()的异同
- Bokeh 添加注释
- pdo query获取mysql单行结果_php代码连不上mysql的可能?看看这个也许能给你点启发...
- 第11章 连接查询和分组查询
- 技术对接场景,打破创新窘境
- NSSM 制作 window 服务
- 学生管理系统测试用例
- 时间序列数据的平稳性检验
- 双代号网络图快速计算时差法
- 各种坐标系下的散度、梯度、旋度公式
- 为基因序列片段在NCBI的GenBank数据库申请登录号
- 国家气象站逐日数据据v3.0 提取转成SWAT所需要格式
- 没有十年网龄都不知道的事儿
- Promise、then()、catch()详解
- Qt 软件开发框架(详细版)
热门文章
- JSPatch源码解读
- 【VMCloud云平台】SCAP(二)
- CXF开发WebService服务器端
- 骑行GPS导航套件:多普达D600+夏新GPS-166+灵图天行者9配合,伴我骑行千里
- 微信公众平台开发中提示“该公众号提供的服务出现故障”问题解决
- linux shell学习-1
- 原根求解算法 NTT算法
- Codeforces Round #354 (Div. 2) A. Nicholas and Permutation
- 用Scheme写Scheme编译器(三):一元运算
- Oracle每日一题——(1) 启动/停止监听