浅谈C++中对象的复制与对象之间的相互赋值

C++对象的复制

有时需要用到多个完全相同的对象,例如,同一型号的每一个产品从外表到内部属性都是一样的,如果要对每一个产品分别进行处理,就需要建立多个同样的对象,并要进行相同的初始化,用以前的办法定义对象(同时初始化)比较麻烦。此外,有时需要将对象在某一瞬时的状态保留下来。

C++提供了克隆对象的方法,来实现上述功能。这就是对象的复制机制。

用一个已有的对象快速地复制出多个完全相同的对象。如

  Box box2(box1);

其作用是用已有的对象box1去克隆出一个新对象box2。

其一般形式为:

  类名 对象2(对象1);

用对象1复制出对象2。

可以看到,它与定义对象的方式类似,但是括号中给出的参数不是一般的变量,而是对象。在建立对象时调用一个特殊的构造函数——复制构造函数(copy constructor)。这个函数的形式是这样的:

//The copy constructor definition.
Box::Box(const Box& b)
{
  height=b.height; width=b.width; length=b.length;
}

复制构造函数也是构造函数,但它只有一个参数,这个参数是本类的对象(不能是其他类的对象), 而且采用对象的引用的形式(一般约定加const声明,使参数值不能改变,以免在调用此函数时因不慎而使对象值被修改)。此复制构造函数的作用就是将实参对象的各成员值一一赋给新的对象中对应的成员。

复制对象的语句

  Box box2(box1);

这实际上也是建立对象的语句,建立一个新对象box2。由于在括号内给定的实参是对象,因此编译系统就调用复制构造函数(它的形参也是对象), 而不会去调用其他构造函数。实参box1的地址传递给形参b(b是box1的引用),因此执行复制构造函数的函数体时,将box1对象中各数据成员的值赋给box2中各数据成员。

如果用户自己未定义复制构造函数,则编译系统会自动提供一个默认的复制构造函数,其作用只是简单地复制类中每个数据成员。C++还提供另一种方便用户的复制形式,用赋值号代替括号,如

  Box box2=box1; //用box1初始化box2

其一般形式为

  类名 对象名1 = 对象名2;

可以在一个语句中进行多个对象的复制。如

  Box box2=box1,box3=box2;

按box1来复制box2和box3。可以看出,这种形式与变量初始化语句类似,请与下面定义变量的语句作比较:

  int a=4,b=a;

这种形式看起来很直观,用起来很方便。但是其作用都是调用复制构造函数。

请注意对象的复制和对象的赋值在概念上和语法上的不同。对象的赋值是对一个已经存在的对象赋值,因此必须先定义被赋值的对象,才能进行赋值。而对象的复制则是从无到有地建立一个新对象,并使它与一个已有的对象完全相同(包括对象的结构和成员的值)。

可以对例子程序中的主函数作一些修改:

int main( )
{
  Box box1(15,30,25); //定义box1
  cout<<"The volume of box1 is "<<box1.volume( )<<endl;
  Box box2=box1,box3=box2; //按box1来复制box2,box3
  cout<<"The volume of box2 is "<<box2.volume( )<<endl;
  cout<<"The volume of box3 is "<<box3.volume( )<<endl;
}

执行完第3行后,3个对象的状态完全相同。

下面说一下普通构造函数和复制构造函数的区别。

1) 在形式上
类名(形参表列); //普通构造函数的声明,如Box(int h,int w,int len);
类名(类名& 对象名); //复制构造函数的声明,如Box(Box &b);

2) 在建立对象时,实参类型不同
系统会根据实参的类型决定调用普通构造函数或复制构造函数。如

  Box box1(12,15,16); //实参为整数,调用普通构造函数
  Box box2(box1); //实参是对象名,调用复制构造函数

3) 在什么情况下被调用
普通构造函数在程序中建立对象时被调用。复制构造函数在用已有对象复制一个新对象时被调用,在以下3种情况下需要克隆对象:
① 程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如上面介绍的那样。

② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。如

void fun(Box b) //形参是类的对象
{ }
int main( )
{
  Box box1(12,15,18);
  fun(box1); //实参是类的对象,调用函数时将复制一个新对象b
  return 0;
}

③ 函数的返回值是类的对象。在函数调用完毕将返回值带回函数调用处时。此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。如

Box f( ) //函数f的类型为Box类类型
{
  Box box1(12,15,18);
  return box1; //返回值是Box类的对象
}
int main( )
{
  Box box2; //定义Box类的对象box2
  box2=f( ); //调用f函数,返回Box类的临时对象,并将它赋值给box2
}

以上几种调用复制构造函数都是由编译系统自动实现的,不必由用户自己去调用,读者只要知道在这些情况下需要调用复制构造函数就可以了。

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

对象之间的赋值也是通过赋值运算符“=”进行的。本来,赋值运算符“=”只能用来对单个的变量赋值,现在被扩展为两个同类对象之间的赋值,这是通过对赋值运算符的重载实现的。

实际这个过程是通过成员复制来完成的,即将一个对象的成员值一一复制给另一对象的对应成员。

对象赋值的一般形式为:

  对象名1 = 对象名2;

注意对象名1和对象名2必须属于同一个类。例如

  Student stud1,stud2; //定义两个同类的对象
  stud2=stud1; //将stud1赋给stud2

通过下面的例子可以了解怎样进行对象的赋值。

[例] 对象的赋值。

#include <iostream>
using namespace std;
class Box
{
  public :
  Box(int =10,int =10,int =10); //声明有默认参数的构造函数
  int volume( );
  private :
  int height;
  int width;
  int length;
};
Box::Box(int h,int w,int len)
{
  height=h;
  width=w;
  length=len;
}
int Box::volume( )
{
  return (height*width*length); //返回体积
}
int main( )
{
  Box box1(15,30,25),box2; //定义两个对象box1和box2
  cout<<"The volume of box1 is "<<box1.volume( )<<endl;
  box2=box1; //将box1的值赋给box2
  cout<<"The volume of box2 is "<<box2.volume( )<<endl; return 0;
}

运行结果如下:

The volume of box1 is 11250
The volume of box2 is 11250

说明:
对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。数据成员是占存储空间的,不同对象的数据成员占有不同的存储空间,赋值的过程是将一个对象的数据成员在存储空间的状态复制给另一对象的数据成员的存储空间。而不同对象的成员函数是同一个函数代码段,不需要、也无法对它们赋值。
类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果 (在此不作详细分析,只需记住这一结论即可)。

时间: 2015-09-14

C++中对象的常引用、动态建立和释放相关知识讲解

C++对象的常引用 我们知道,一个变量的引用就是变量的别名.实质上,变量名和引用名都指向同一段内存单元. 如果形参为变量的引用名,实参为变量名,则在调用函数进行虚实结合时,并不是为形参另外开辟一个存储空间(常称为建立实参的一个拷贝), 而是把实参变量的地址传给形参(引用名),这样引用名也指向实参变量. [例] 对象的常引用. #include <iostream> using namespace std; class Time { public: Time(int,int,int); int

详解C++中的this指针与常对象

C++ this指针详解 this 是C++中的一个关键字,也是一个常量指针,指向当前对象(具体说是当前对象的首地址).通过 this,可以访问当前对象的成员变量和成员函数. 所谓当前对象,就是正在使用的对象,例如对于stu.say();,stu 就是当前对象,系统正在访问 stu 的成员函数 say(). 假设 this 指向 stu 对象,那么下面的语句中,this 就和 pStu 的值相同: Student stu; //通过Student类来创建对象 Student *pStu = &s

解析C++中指向对象的指针使用

C++指向对象的常指针 将指针变量声明为const型,这样指针值始终保持为其初值,不能改变. 如: Time t1(10,12,15),t2; //定义对象 Time * const ptr1; //const位置在指针变量名前面,规定ptr1的值是常值 ptr1=&t1; //ptr1指向对象t1,此后不能再改变指向 ptr1=&t2; //错误,ptr1不能改变指向 定义指向对象的常指针的一般形式为: 类名 * const 指针变量名; 也可以在定义指针变量时使之初始化,如将上面第2,

C++中指向对象的常指针与指向常对象的指针详解

指向对象的常指针 将指向对象的指针变量声明为const型,并使之初始化,这样指针值始终保持为其初始值,不能改变. 复制代码 代码如下: Time t1(10,12,15),t2;Time * const ptr1=&t1;ptr1=&t2; 定义指向对象的常指针的一般形式为 类名    *    const    指针变量=对象地址; 注意应该在定义指针变量时使之初始化 指向对象的常指针变量的值不能被改变,即始终指向同一个对象,但可以改变其所指向对象中的数据成员(非const型)的值. 往

深入源码解析Python中的对象与类型

对象 对象, 在C语言是如何实现的? Python中对象分为两类: 定长(int等), 非定长(list/dict等) 所有对象都有一些相同的东西, 源码中定义为PyObject和PyVarObject, 两个定义都有一个共同的头部定义PyObject_HEAD(其实PyVarObject有自己的头部定义PyObject_VAR_HEAD, 但其实际上用的也是PyObject_HEAD). 源码位置: Include/object.h PyObject_HEAD Python 内部, 每个对象拥

解析c++中参数对象与局部对象的析构顺序的详解

下面是c++的源码: 复制代码 代码如下: class X  {public:   int i;   int j;   ~X() {} };void f(X x) {  X x1;  x.i = 1;  x.j = 2; }int main() {    f(X());} 下面是main函数的汇编码: 复制代码 代码如下: _main    PROC ; 15   : int main() { push    ebp    mov    ebp, esp    sub    esp, 8;为临时

C++指向函数的指针实例解析

通常来说C++函数指针是指指向函数的指针,而非指向对象的指针.就像其他指针一样,函数指针也指向某个特定的类型.函数类型由其返回类型以及形参表确定,而与函数名无关. 定义: char (*fP)(char,int); 赋值: char function(char i,int j) { } fp=function; 调用 (*fp)(10,100); type char (*FUN)(char,int);//类型定义 FUN fp ;//定义fp为指向函数的指针 volatile的用法和作用: co

C++中的对象指针总结

指向对象的指针在建立对象的时候,变异系统会给每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 一个简单的示例1.1: 复制代码 代码如下: #include<iostream>using namespace std;class Student{ public:  int num;  int score;  Student(int ,int );//声明构造函数  void Print();//声明输出信息函数};Stude

详解C++中的对象指针与对象数组

C++对象指针 指向对象的指针 在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 如果有一个类: class Time { public : int hour; int minute; int sec; void get_time( ); }; void Time::get_time( ) { cout<<hour<<":"<<minute<<

解析Python中的变量、引用、拷贝和作用域的问题

在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样.在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没有赋值,那么Python认为这个变量没有定义.如下: >>> a Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'a'

深入解析C++中的引用类型

c++比起c来除了多了类类型外还多出一种类型:引用.这个东西变量不象变量,指针不象指针,我以前对它不太懂,看程序时碰到引用都稀里糊涂蒙过去.最近把引用好好地揣摩了一番,小有收获,特公之于社区,让初学者们共享. 引用指的是对一个对象的引用.那么什么是对象?在c++中狭义的对象指的是用类,结构,联合等复杂数据类型来声明的变量,如 MyClass myclass,CDialog  mydlg,等等.广义的对象还包括用int,char,float等简单类型声明的变量,如int a,char b等等.我在

C++中返回指向函数的指针示例

在C++中,函数的形参可以是指向函数的指针,函数也可以返回函数的指针.例如:int (*ff(int)) (int *,int);表示:ff(int)是一个函数,带有一个int型的形参,该函数返回int (*) (int *,int),它是一个指向函数的指针,所指向的函数返回int型并带有两个分别是Int*和int型的形参.使用typedef可使得定义更加易懂:typedef int (*PF) (int *,int);PF ff(int);下面给出一个例子: 复制代码 代码如下: #inclu