C++类的复制构造函数

复制构造函数的形式为:

class ClassName
{
...

ClassName(ClassName&)
{
...
}
或
ClassName(const ClassName&)
{
...
}
...
};

如果没有定义复制构造函数,则编译器会为该类自动生成复制构造函数。如果用户自定义了任何一个类型的复制构造函数,则不再生成默认的复制构造函数。

生成的默认构造函数只是简单地进行值的复制。对于包含指向动态分配的内存空间的指针变量来说,复制构造函数只是将指针变量的值复制给新的变量,而使得两个对象中的变量指向同一个内存空间。这就会造成数据之间的耦合,甚至会出现多次删除同一个指针的问题,会产生严重的错误。因此,对于包含这类成员变量的类来说,其复制构造函数需要自定义,需要为新的对象中的变量分配新的内存空间,并根据源对象的变量的值填充该内存空间。与以值复制相比,这种类型的复制成为“深拷贝”,而以值的复制的形式称为“浅拷贝”。

/*****************************************
 * copy_constructor.cpp                  *
 *                                       *
 * C++类的复制构造函数(浅拷贝带来的多次释放内存 *
 * 错误                                   *
 ****************************************/


class Test
{
private:
  int i;
  int *p;

public:
  Test()
  {
    i = 10;
    p = new int(5);
  }

  //浅拷贝
  Test(const Test& t)
  {
    i = t.i;
    p = t.p;
  }

  ~Test()
  {
    delete p;
  }

};

int main()
{
  Test t;
  Test t1(t);

  return 0;
}

多次释放错误

/****************************************
 * copy_constructor_2.cpp               *
 *                                      *
 * C++类的复制构造函数(深拷贝)          *
 ****************************************/

#include <iostream>
using namespace std;

class Test
{
private:
  int i;
  int *p;

public:
  Test()
  {
    i = 10;
    p = new int(5);
  }

  //深拷贝
  Test(const Test& t)
  {
    i = t.i;
    p = new int(*t.p);
  }

  ~Test()
  {
    delete p;
  }

public:
  void Print()
  {
    cout<<"i = "<<i<<","<<"*p = "<<*p<<endl;
  }
};

int main()
{
  Test t;
  t.Print();
  Test t1(t);
  t1.Print();
  return 0;
}

深拷贝
复制构造函数应用于以下三种情况:

  • 使用一个对象去初始化同类的另一个对象
  • 在类的对象作为函数参数的情况下,当函数调用时,其复制构造函数被调用
  • 当类的对象作为函数的返回值时,当函数返回时,该类的复制构造函数被调用。
/****************************************
 * copy_constructor_3.cpp               *
 *                                      *
 * 复制构造函数应用的三种情况           *
 ****************************************/

#include <iostream>
using namespace std;

class A
{
private:
  static int id;
public:
  int value;
public:
  A(int value)
  {
    id++;
    this->value = value;
  }

  A(const A& a)
  {
    value = a.value;
    id++;
    cout<<"复制构造函数(id)"<<id<<"被调用"<<endl;
  }

};

int A::id = 0;

void UsingAAsPara(A a)
{
}

A UsingAAsReturn()
{
  A a(15);
  return a;
}

int main()
{
  A a(10);

  cout<<"A b(a): ";
  A b(a);
  cout<<"A c = a:";
  A c = a;
  cout<<"UsingAAsPara(c)";
  UsingAAsPara(c);
  cout<<"UsingAAsReturn()";
  cout<<UsingAAsReturn().value<<endl;
  return 0;
}

使用情况
使用g++编译器发现第三种情况下并没有调用复制构造函数,而是使用的带参数value的构造函数。可见,g++编译器对这种情况进行了优化,见下面的标准中的描述。

12.8 When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.118) This elision of copy operations is permitted in the following circumstances (which may be combined
to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value
— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy
[ Example:
class Thing {
    public :
        Thing ();
        ~ Thing ();
        Thing ( const Thing &);
};
Thing f () {
    Thing t;
    return t;
}
Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing: the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2. Effectively, the construction of the local object t can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program exit. —end example ]

参考文献

https://www.coursera.org/course/pkupop

### C++复制构造函数 #### 定义 复制构造函数是一种特殊的构造函数,其作用是在创建新对象时通过已有的对象初始化该新对象。此构造函数的名字必须与名相同,并且接受一个同型的常量引用作为唯一参数[^1]。 对于 `CExample` 而言: ```cpp class CExample { public: // 构造函数和其他成员... // 自定义的拷贝构造函数 CExample(const CExample& other); }; ``` 上述代码展示了如何声明一个名为 `CExample` 的及其对应的复制构造函数。 #### 使用场景 当满足特定条件之一时,编译器会自动调用复制构造函数: - 当使用一个对象去初始化另一个同的对象; - 函数返回值为型并按值传递; - 将临时对象赋给某个型的变量; 这些情况下如果用户没有提供自己的版本,则编译器将会合成默认的行为[^3]。 #### 实现细节 为了正确处理资源管理(比如动态分配内存),通常需要实现深拷贝而非浅拷贝。下面是一个简单的例子说明这一点: 假设有一个包含指针成员的 `StringHolder`: ```cpp #include <cstring> #include <iostream> class StringHolder { private: char* data; public: explicit StringHolder(const char* str) : data(new char[strlen(str)+1]) { strcpy(data, str); } ~StringHolder() { delete[] data; } /// @brief 显式的拷贝构造函数实现了深拷贝逻辑 StringHolder(const StringHolder &other):data(nullptr){ size_t length = strlen(other.data); this->data = new char[length + 1]; strncpy(this->data, other.data, length + 1); } void showData() const { std::cout << "Data: " << (this->data ? this->data:"null") << '\n'; } }; int main(){ StringHolder sh1("Hello"); StringHolder sh2(sh1); sh1.showData(); // 输出 Hello sh2.showData(); // 同样输出 Hello return 0; } ``` 在这个例子中,`StringHolder` 拥有指向字符数组的指针 `data`. 如果不特别编写复制构造函数,默认行为只会简单地复制这个指针地址——即所谓的“浅拷贝”。这可能导致两个不同对象共享同一块堆上分配的空间,在其中一个被销毁之后留下悬空指针的风险。因此这里提供了自定义的复制构造函数来进行完整的字符串副本操作,也就是执行了所谓“深拷贝”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值