本系列文章主要介绍 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. 对象的赋值和复制(转)

    一.对象的赋值和复制 1.对象的赋值 如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的值可以赋给另一个同类的对象.这里所指的对象的值是指对象中所有数据成员的值. ...

  2. C++对象的赋值和复制

    C++对象的赋值 1.1对象之间的赋值是用"="运算符来实现的,"="在c++中扩展为重载运算符来实现对象间的赋值. t1=t2; 1.2对象赋值是对数据成员的 ...

  3. java 反射创建对象并赋值_[原创] Java JDBC连接数据库,反射创建实体类对象并赋值数据库行记录(支持存储过程)...

    1 import java.lang.reflect.*;2 import java.sql.*;3 import java.util.*;4 5 public classSqlHelper {6 / ...

  4. C++派生类对象和基类对象赋值

    在C++中,我们允许 将派生类对象赋给基类对象.(不允许将基类对象赋给派生类对象) 只会将基类对象成员赋值 用基类指针指向派生类对象.(不允许用派生类指针指向基类对象) 基类指针只能操作基类中的成员 ...

  5. C++:类对象的复制和赋值

    //例1.类对象复制,拷贝构造函数#include<iostream> using namespace std;class Student { public:Student(){cout ...

  6. (一二四)给类对象赋值、以及类对象的返回值

    于直接给对象赋值: 之前学过,如何给对象在初始化时进行赋值. 对于C++11来说,初始化方式有三种: ① man c = man{ "cc",1 }; ② man d = { &q ...

  7. java类向拦截器传值_MyBatis拦截器:给参数对象属性赋值的实例

    该拦截器的作用:在进行增加.修改等操作时,给数据模型的一些通用操作属性(如:创建人.创建时间.修改人.修改时间等)自动赋值. 该实现是在dao层拦截,即存入db前最后一层.后经分析,不是很合理,改为在 ...

  8. 类选择器遍历赋值_利用反射实现配置表数据到类对象数据的转换

    在游戏开发中,配置表是不可少的.通常我们将一个类,做成一个配置表,将配置表每列的索引都和类的字段名严格对应起来. 先实例化一个类的对象,然后通过反射来遍历类中的字段,通过field.SetValue( ...

  9. c++,派生类对象可以对基类赋值,基类对派生类不可以赋值

    派生类对象可以对基类对象赋值,赋值时属于派生类独有的部分就舍弃不用. #include <iostream> using namespace std;class DemoA { publi ...

  10. python类对象赋值_Python对象赋值、浅拷贝、深拷贝

    Python中,基本数据类型,理解为常见数据类型:布尔型.整型.浮点型.字符串.列表.元组.字典.集合,随语言不同而不同,但是根据在内存中存储方式的不同,区分开原子类型和容器类型. 对象赋值 对象的赋 ...

最新文章

  1. 关于共用体所占的内存空间的问题
  2. 国防科技大学教授:殷建平——计算机科学理论的过去、现在与未来
  3. 用symbol来获得ShadowSSDT的原始地址和函数名
  4. java图形化界面设置焦点_如何在更新窗口时防止Java图形程序窃取焦点?
  5. aop 获取方法入参出参_ASM字节码编程 | JavaAgent+ASM字节码插桩采集方法名称及入参和出参结果并记录方法耗时...
  6. var_dump() 与 print_r()的异同
  7. Bokeh 添加注释
  8. pdo query获取mysql单行结果_php代码连不上mysql的可能?看看这个也许能给你点启发...
  9. 第11章 连接查询和分组查询
  10. 技术对接场景,打破创新窘境
  11. NSSM 制作 window 服务
  12. 学生管理系统测试用例
  13. 时间序列数据的平稳性检验
  14. 双代号网络图快速计算时差法
  15. 各种坐标系下的散度、梯度、旋度公式
  16. 为基因序列片段在NCBI的GenBank数据库申请登录号
  17. 国家气象站逐日数据据v3.0 提取转成SWAT所需要格式
  18. 没有十年网龄都不知道的事儿
  19. Promise、then()、catch()详解
  20. Qt 软件开发框架(详细版)

热门文章

  1. JSPatch源码解读
  2. 【VMCloud云平台】SCAP(二)
  3. CXF开发WebService服务器端
  4. 骑行GPS导航套件:多普达D600+夏新GPS-166+灵图天行者9配合,伴我骑行千里
  5. 微信公众平台开发中提示“该公众号提供的服务出现故障”问题解决
  6. linux shell学习-1
  7. 原根求解算法 NTT算法
  8. Codeforces Round #354 (Div. 2) A. Nicholas and Permutation
  9. 用Scheme写Scheme编译器(三):一元运算
  10. Oracle每日一题——(1) 启动/停止监听