拷贝构造函数的使用

本文解释了拷贝构造函数的概念及应用场景,包括函数参数按值传递、返回对象初始化另一个对象以及直接调用拷贝构造函数。并通过示例代码展示了拷贝构造函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.为什么会有拷贝构造函数

#include <fstream>
#include 
<string>
using namespace std;

ofstream 
out("HowMany.out");

class HowMany{
private:
       
static int objectCount;
public:
       HowMany() { objectCount
++; }
       
static void print(const string& msg="")
       {
           
if(msg.size()!=0)
                 
out<<msg<<"";
           
out<<"objectCount= "<<objectCount<<endl;
       }
       
~HowMany()
       {
            objectCount
--;
            print(
"~Howmany()");
        }
};
int HowMany::objectCount=0;

HowMany f(HowMany x)
{
     x.print(
"x argument inside f()");
    
return x;
}

int main()
{
      HowMany h;
      HowMany::print(
"after construction of h");
      HowMany h2
=f(h);
      HowMany::print(
"after call to f()");
}

        这里是上面的代码的输出: 
               after construction of h: objectCount= 1
               x argument inside f(): objectCount= 1
               ~Howmany(): objectCount= 0
               after call to f(): objectCount= 0
               ~Howmany(): objectCount= -1
               ~Howmany(): objectCount= -2

      为什么会出现这种情况呢?一定是什么地方出了问题。很明显是在函数f 里面。
      它是按值传递的参数,这样就会得到原来对象的一份拷贝,是临时的对象,出了f 的范围这个拷贝自动调用析构销毁,并且将对象计数objectCount减一。但是在传递参数的时候调用构造函数时应该将对象计数objectCount加一的啊,这里却没有。说明在函数传递参数的时候没用调用构造函数,而是用的另外一种方式传递的参数,就是拷贝构造函数。

 二.怎么用拷贝构造函数

      当通过按值传递的方式传递一个对象时,就创立了一个新对象。在下面三种情况会用拷贝构造函数来初始化对象。
         1. 函数参数按值传递  (和上面那个例程一样)
         2. 返回一个对象来初始化另外一个对象
                      HowMany f(HowMany t);             //函数f 返回HowMany对象
                      HowMany h1;
                      HowMany h2=f(h1);
         3. 直接调用拷贝构造函数
                       HowMany  h1;
                       HowMany  h2(h1);
    拷贝构造函数和普通的构造函数差不多,都是以类的名字为函数名,无返回值,只是参数有些不同。
       形式为 X(X&)

#include <fstream>
#include 
<string>
using namespace std;

ofstream 
out("HowMany2.out");

class HowMany2{
private:
     
string name;
     
static int objectCount;
public:
     HowMany2(
const string& id=""):name(id)
     {
          
++objectCount;
          print(
"HowMany2()");
     }
     
void print(const string& msg=""const
     {
            
if(msg.size()!=0)
                 
out<<msg<<endl;
            
out<<" "<<name<<""<<"objectCount = "<<objectCount<<endl;
     }
     HowMany2(
const HowMany2& h)
     {
            name
=h.name;
            name
+=" copy";
            
++objectCount;
            print(
"HowMany2(const HowMany2&)");
      }
      
~HowMany2()
      {
             objectCount
--;
             print(
"~Howmany2()");
      }
};

int HowMany2::objectCount=0;

HowMany2 f(HowMany2 x)
{
       x.print(
"x argument inside f()");
       
out<<"Returning from f()"<<endl;
       
return x;
}

int main()
{
        HowMany2 h(
"h");
        
out<<"Entering f()"<<endl;
        HowMany2 h2
=f(h);
        h2.print(
"h2 after call to f()");
        
out<<"Call f(), no return value"<<endl;
        f(h);
        
out<<"After call to f()"<<endl;
        HowMany2 h4(h2);
        h4.print(
"h4 initial");
}

             这里演示了上述的三种需要使用拷贝构造函数的情况,大家可以仔细分析一下输出:
              HowMany2(const HowMany2&)
              h copy: objectCount = 2
              x argument inside f()
              h copy: objectCount = 2
              Returning from f()
              HowMany2(const HowMany2&)
              h copy copy: objectCount = 3
              ~Howmany2()
               h copy: objectCount = 2
               h2 after call to f()
               h copy copy: objectCount = 2
              Call f(), no return value
              HowMany2(const HowMany2&)
              h copy: objectCount = 3
              x argument inside f()
              h copy: objectCount = 3
              Returning from f()
              HowMany2(const HowMany2&)
              h copy copy: objectCount = 4
              ~Howmany2()
              h copy copy: objectCount = 3
              ~Howmany2()
              h copy: objectCount = 2
              After call to f()
              HowMany2(const HowMany2&)
              h copy copy copy: objectCount = 3
              h4 initial
              h copy copy copy: objectCount = 3
              ~Howmany2()
              h copy copy copy: objectCount = 2
              ~Howmany2()
              h copy copy: objectCount = 1
              ~Howmany2()
              h: objectCount = 0

 三、默认拷贝构造函数

          在我们没有为类建立拷贝构造函数的时候,编译器会自动为新类创建一个默认拷贝构造函数,就如第一个例程,但它使用的是简单位拷贝。就是将原始对象的内存简单复制一份,放在新对象的地址上。但这种位拷贝在出现了继承和组合时会出现很多问题。所以一般在写的类稍复杂一点时,我们就必须添加拷贝构造函数。
         不过有时因为初始化很困难,自己很难写出新的拷贝构造函数,而默认拷贝构造函数也不能达到我们的要求,这时我们可以强制编译器不允许按值传递对象,“声明一个私有拷贝构造函数”,甚至不必去定义它。          

class NoCC
{
private:
      
int i;
      NoCC(
const NoCC&);          //私有的拷贝构造函数
public:
      NoCC(
int ii=0):i(ii) {}
};

void f(NoCC);

int main()
{
      NoCC n;
      f(n)      
//error
      NoCC n2=n;       //error
      NoCC n3(n);       //error
}
    

 

结语:

        在以后出现了继承和组合时,涉及到类的层次结构,对象的按值传递是一个很麻烦的事情。拷贝构造函数的重要性将会在那里得到体现。就会有深复制和浅复制两种方式来实现拷贝构造函数。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值