理解拷贝构造函数(引用)

news/2024/7/4 1:43:30 标签: class, delete, 编译器, null, c
cle class="tags" href="/tags/CLASS.html" title=class>class="baidu_pl">
cle_content" class="tags" href="/tags/CLASS.html" title=class>class="article_content clearfix">
content_views" class="tags" href="/tags/CLASS.html" title=class>class="htmledit_views">

 理解拷贝构造函数(引用)

  拷贝构造函数࿰c;是一种特殊的构造函数࿰c;它由class="tags" href="/tags/BianYiQi.html" title=编译器>编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的 (const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话࿰c;它将用常量 方式调用࿰c;另外࿰c;也可以用非常量方式调用。
  在C++中࿰c;下面三种对象需要调用拷贝构造函数:
  1) 一个对象以值传递的方式传入函数体;
  2) 一个对象以值传递的方式从函数返回;
  3) 一个对象需要通过另外一个对象进行初始化;
  如果在前两种情况不使用拷贝构造函数的时候࿰c;就会导致一个指针指向已经被删除的内存空间。对于 第三种情况来说࿰c;初始化和赋值的不同含义是构造函数调用的原因。事实上࿰c;拷贝构造函数是由普通构造函数和赋值操作符共同实现的。描述拷贝构造函数和赋值运 算符的异同的参考资料有很多。
  拷贝构造函数不可以改变它所引用的对象࿰c;其原因如下:当一个对象以传递值的方式传一个函数的时 候࿰c;拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数࿰c;它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传 入它自己的拷贝构造函数࿰c;这会导致无限循环直至栈溢出(Stack Overflow)。除了当对象传入函数的时候被隐式调用以外࿰c;拷贝构造函数在对象被函数返回的时候也同样的被调用。
  如果在类中没有显式的声明一个拷贝构造函数࿰c;那么࿰c;class="tags" href="/tags/BianYiQi.html" title=编译器>编译器会自动生成一个来进行对象之间的位拷 贝(Bitwise Copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对成员的关联方式。显式 声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数࿰c;除非另外一个构造函数在类初始化或构造列表的时候被调用。
  拷贝构造函数使程序更有效率࿰c;因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格࿰c;即使是编译系统会自动为你生成默认拷贝构造函数。事实上࿰c;默认拷贝构造函数可以应付许多情况。
  以下讨论中将用到的例子:
  class="tags" href="/tags/CLASS.html" title=class>class CExample
  {
  public:
  CExample(){pBuffer=NULL; nSize=0;}
  ~CExample(){class="tags" href="/tags/DELETE.html" title=delete>delete pBuffer;}
  void Init(int n){ pBuffer=new char[n]; nSize=n;}
  private:
  char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
  int nSize;
  };
  这个类的主要特点是包含指向其他资源的指针。
  pBuffer指向堆中分配的一段内存空间。
  一、拷贝构造函数
  int main(int argc, char* argv[])
  {
  CExample theObjone;
  theObjone.Init(40);
  //现在需要另一个对象,需要将他初始化称对象一的状态
  CExample theObjtwo=theObjone;
  ...
  }
  语句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
  其完成方式是内存拷贝࿰c;复制所有成员的值。
  完成后࿰c;theObjtwo.pBuffer==theObjone.pBuffer。
  即它们将指向同样的地方࿰c;指针虽然复制了࿰c;但所指向的空间并没有复制࿰c;而是由两个对象共用了。这样不符合要求࿰c;对象之间不独立了࿰c;并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况。
  回顾以下此语句的具体过程:首先建立对象theObjtwo࿰c;并调用其构造函数࿰c;然后成员被拷贝。
  可以在构造函数中添加操作来解决指针成员的问题。
  所以C++语法中除了提供缺省形式的构造函数外࿰c;还规范了另一种特殊的构造函数:拷贝构造函数࿰c;上面的语句中࿰c;如果类中定义了拷贝构造函数࿰c;这对象建立时࿰c;调用的将是拷贝构造函数࿰c;在拷贝构造函数中࿰c;可以根据传入的变量࿰c;复制指针所指向的资源。
  拷贝构造函数的格式为:构造函数名(对象的引用)
  提供了拷贝构造函数后的CExample类定义为:
  class="tags" href="/tags/CLASS.html" title=class>class CExample
  {
  public:
  CExample(){pBuffer=NULL; nSize=0;}
  ~CExample(){class="tags" href="/tags/DELETE.html" title=delete>delete pBuffer;}
  CExample(const CExample&); //拷贝构造函数
  void Init(int n){ pBuffer=new char[n]; nSize=n;}
  private:
  char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
  int nSize;
  };
  CExample::CExample(const CExample& RightSides) //拷贝构造函数的定义
  {
  nSize=RightSides.nSize; //复制常规成员
  pBuffer=new char[nSize]; //复制指针指向的内容
  memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
  }
  这样࿰c;定义新对象࿰c;并用已有对象初始化新对象时࿰c;CExample(const CExample& RightSides)将被调用࿰c;而已有对象用别名RightSides传给构造函数࿰c;以用来作复制。原则上࿰c;应该为所有包含动态分配成员的类都提供拷贝 构造函数。
  下面介绍拷贝构造函数的另一种调用。当对象直接作为参数传给函数时࿰c;函数将建立对象的临时拷贝࿰c;这个拷贝过程也将调同拷贝构造函数。例如:
  BOOL testfunc(CExample obj);
  testfunc(theObjone); //对象直接作为参数。
  BOOL testfunc(CExample obj)
  {
  //针对obj的操作实际上是针对复制后的临时拷贝进行的
  }
  还有一种情况࿰c;也是与临时对象有关的
  当函数中的局部对象被被返回给函数调者时࿰c;也将建立此局部对象的一个临时拷贝࿰c;拷贝构造函数也将被调用
  CTest func()
  {
  CTest theTest;
  return theTest;
  }
  二、赋值符的重载
  下面的代码与上例相似
  int main(int argc, char* argv[])
  {
  CExample theObjone;
  theObjone.Init(40);
  CExample theObjthree;
  theObjthree.Init(60);
  //现在需要一个对象赋值操作,被赋值对象的原内容被清除࿰c;并用右边对象的内容填充。
  theObjthree=theObjone;
  return 0;
  }
  也用到了"="号࿰c;但与"一、"中的例子并不同࿰c;"一、"的例子中࿰c;"="在对象声明语句中࿰c;表示初始化。更多时候,这种初始化也可用括号表示。
  例如 CExample theObjone(theObjtwo);
  而本例子中࿰c;"="表示赋值操作。将对象theObjone的内容复制到对象theObjthree;࿰c;这其中涉及到对象theObjthree原有内容的丢弃࿰c;新内容的复制。
  但"="的缺省操作只是将成员变量的值相应复制。旧的值被自然丢弃。
  由于对象内包含指针࿰c;将造成不良后果:指针的值被丢弃了࿰c;但指针指向的内容并未释放。指针的值被复制了࿰c;但指针所指内容并未复制。
  因此࿰c;包含动态分配成员的类除提供拷贝构造函数外࿰c;还应该考虑重载"="赋值操作符号。
  类定义变为:
  class="tags" href="/tags/CLASS.html" title=class>class CExample
  {
  ...
  CExample(const CExample&); //拷贝构造函数
  CExample& operator = (const CExample&); //赋值符重载
  ...
  };
  //赋值操作符重载
  CExample & CExample::operator = (const CExample& RightSides)
  {
  nSize=RightSides.nSize; //复制常规成员
  char *temp=new char[nSize]; //复制指针指向的内容
  memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
  class="tags" href="/tags/DELETE.html" title=delete>delete []pBuffer; //删除原指针指向内容 (将删除操作放在后面࿰c;避免X=X特殊情况下࿰c;内容的丢失)
  pBuffer=NULL;
  pBuffer=temp; //建立新指向
  return *this
  }
  三、注意事项
  常见错误:
  拷贝构造函数使用赋值运算符重载的代码。
  CExample::CExample(const CExample& RightSides)
  {
  *this=RightSides //调用重载后的"="
  }
  而这样会造成循环调用重载后的"="和拷贝构造函数࿰c;最后造成栈溢出(Stack Overflow)。
  四࿰c;拷贝构造函数与构造函数的区别
  class="tags" href="/tags/CLASS.html" title=class>class 类名
  {
  public
  类名(形参参数)//构造函数
  类名(类名&对象名)//拷贝构造函数
  ,,,,,,,,,,,,,,,,,,,,,
  };
  拷贝构造函数的实现:
  类名::类名(类名&对象名)//拷贝构造函数的实现
  {函数体}
  不完整的例子
  拷贝构造函数:
  Class Point
  {
  Public:
  Point(int xx=0,int yy=m)(X=xx;Y=yy;)
  Point(Point& p);
  Int getX() {return X;}
  Int getY(){ return Y;}
  Private :
  Int X,Y;
  }
  Point::Point(Point& p)
  {
  X=p.X;
  Y=p.Y;
  Cout<<"拷贝构造函数吊样"<<endl;
  }

cle>

http://www.niftyadmin.cn/n/489629.html

相关文章

拷贝构造函数和拷贝赋值运算符安全漏洞分析(引用)

拷贝构造函数和拷贝赋值运算符安全漏洞分析&#xff08;引用&#xff09; l 概述 在c语言程序设计中&#xff0c;如果某个类的对象持有动态分配的内存资源&#xff0c;但是程序员却没有为该类定义拷贝构造函数和拷贝赋值运算符&#xff0c;则当对象拷贝构造(新对象尚未存在…

运算符重载的规则

运算符重载的规则 1 C中的运算符除了少数几个(类属关系运算符“.”、作用域分辨符“::”、成员指针运算符“二”、sized运算符和三目运算符“?:”)之外&#xff0c;全部 可以重载&#xff0c;而且只能重载C中已有的运算符&#xff0c;不能臆造新的运算符(比如定义**来代表乘…

高级文件编程——标准文件操作的函数(转)

高级文件编程——标准文件操作的函数(转) 本节所讲的文件读写函数均是指顺序读写, 即读写了一条信息后, 指针自动 加1。下面分别介绍写操作函数和读操作函数。 1. 文件的顺序写函数 fprintf()、fputs()和fputc()函数 函数fprintf()、fputs()和fputc()均为文件…

高精度算法(引用)

高精度算法(引用) 在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字.   一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算.   譬如一个很大的数字N > 10^ 100, 很显…

高精度运算(转)

高精度运算(转) 目录 整数 整数表示 进制转换 四则运算 快速乘法 一般原理 Karatsuba乘法 Toom-Cook乘法 FFT乘法 我们所熟知的科学计算一般就是指数值计算,数值计算是计算数学的一个主要部分,它研究用计算机求解各种数学问题的数值计算方法及其理论与软件实现[1] , 关于数值计…

大数四则运算的C++实现(转)

大数四则运算的C实现(转) [摘要] 大数运算不仅仅运用在密码学中&#xff0c;还运用在一些物理学研究、生物学&#xff0c;化学等科目中。大数运算&#xff0c;意味着参加的值和计算结果通常是以上百位数&#xff0c;上千位数以及更大 长度之间的整数运算。例如大家所熟知圆周…

幻方阵(转)

幻方阵(转) 幻方是什么呢&#xff1f;如右图就是一个幻方&#xff0c;即将n*n&#xff08;n>3&#xff09;个数字 放入n*n的方格内&#xff0c;使方格的各行、各列及对角线 上各数字之各相等。   我很早就对此非常感兴趣&#xff0c;也有所收获。   8 1 6   3 5 7 …

详细解析Java中抽象类和接口的区别 abstract class和interface(转)

详细解析Java中抽象类和接口的区别 abstract class和interface(转) 在 Java语言中&#xff0c; abstract class 和interface 是支持抽象类定义的两种机制。正是由于这两种机制的存在&#xff0c;才赋予了Java强大的 面向对象能力。abstract class和interface之间在对于抽象类…