C++实操 - 拷贝构造函数

什么是拷贝构造函数(copy constructor)?

拷贝构造函数是一个类或结构体的特殊构造函数,用于复制一个现有的实例。

一个类名为T的类的复制构造函数是一个非模板构造函数,其第一个参数是T&、const T&、volatile T&或const volatile T&,并且没有其他参数,或者其余参数都有默认值。

根据C++的标准,MyClass的拷贝构造函数(只能有一个)的声明必须是下面之一:

  MyClass( const MyClass& other );

  MyClass( MyClass& other );

  MyClass( volatile const MyClass& other );

  MyClass( volatile MyClass& other );

而下面这些构造函数并不是拷贝构造函数,尽管作用和拷贝构造函数类似:

  MyClass( MyClass* other );

  MyClass( const MyClass* other )

什么时候需要写一个拷贝构造函数?

首先,你应该明白,如果你不声明一个拷贝构造函数,编译器会隐含地给你生成一个默认的。

这个隐含的拷贝构造函数会对源对象进行成员式拷贝。

例如,给定一个类:

class MyClass {

   int x;

   char c;

   std::string s;

};

编译器提供的拷贝构造函数完全等同于:

  MyClass::MyClass( const MyClass& other ) :

     x( other.x ), c( other.c ), s( other.s )

  {}

在许多情况下,这已经足够了。然而,在某些情况下,仅成员式拷贝是不够的。

到目前为止,默认的拷贝构造函数不行的最常见的原因,是由于对象包含原始指针,需要对指针进行 "深度 "拷贝。

也就是说,你不希望拷贝指针本身;相反,你想拷贝指针的指向的东西。

为什么你需要进行 "深度 "拷贝?

这是因为通常实例拥有该指针,也就是说,实例负责对该指针调用删除命令进行内存释放,可能是在析构器或其他时刻。

如果两个对象最终在同一个非空指针上调用删除操作,就会导致堆损坏。

你很少会遇到不包含原始指针的类,所以默认的拷贝构造函数一般是不够的。

语法

class-name ( const class-name & ) (1)

class-name ( const class-name & ) = default; (2) (since C++11)

class-name ( const class-name & ) = delete; (3) (since C++11)

其中class-name是当前类的名字(或类模板的当前实例),或者,当在命名空间范围内或在友方声明中声明时,它必须是一个合格的类名。

上面三种情况说明:

1) 复制构造函数的典型声明。

2) 强制由编译器生成一个拷贝构造函数。这样用户无法再重载或定义自己的拷贝构造函数。

3) 避免隐式生成拷贝构造函数。

每当一个对象从另一个相同类型的对象初始化(通过直接初始化或复制初始化)时,就会调用拷贝构造函数(除非重载解析选择了更好的匹配或调用被省略)。

使用复制构造函数有三种情况:

a. 初始化: T a = b;或者T a(b);,其中b是T的类型。

b. 函数参数传递:f(a);,其中a是T类型的,f是void f(T t)。

c. 函数返回:返回a;在一个函数里面,比如T f(),其中a是T类型.

其中b的情况是,调用函数f时,会构造一个对象作为参数传递给函数。

c的情况是,函数返回一个对象时,也会构造一个对象,并返回。

我们来使用代码验证一下:

copyctor.cpp



#include <cstdio>


class A
{
public:

    A(const A& obj){printf("Copy ctor called.\n"); }
    A(){printf("Normal ctor called.\n");}

};


void parameterA(A a)
{


}

int main()
{
  A a;
  printf("===============\n");
  A b = a;
  printf("===============\n");
  A c(a);
  printf("===============\n");
  parameterA(a);

}



$ g++ -o copyctor copyctor.cpp
$ ./copyctor
Normal ctor called.
===============
Copy ctor called.
===============
Copy ctor called.
===============
Copy ctor called.

关于拷贝构造函数的参数

在拷贝构造函数的的声明中,其参数前面可以加const也可以不加。

比如:

MyClass( const MyClass& other );

MyClass( MyClass& other );

这两种声明方式都是可以的。

但最好加上const,因为这里使用的是引用参数,而在C++标准里规定非常量引用类型是不能引用临时变量的。

理解起来就是,你把临时对象作为引用传进来,如果不加const,那就会修改这个对象,但临时对象的生命周期管理会引起问题。

所以加上const的引用类型,来作为函数参数,才能将临时对象传进来。

并且拷贝构造函数,一般也不需要对源对象做修改。

那么临时对象是什么样的?

比如:

std::string("Hello!");

这就是一个没有名字的临时对象。

我们来写个代码体验一下:

#include <cstdio>

#include <string>



void printStringNoConst(std::string & s)

{

  printf("%s\n", s.c_str());

}

void printStringConst(const std::string & s)

{

  printf("%s\n", s.c_str());

}



int main()

{

  std::string txt = "Hello!";



  printStringNoConst(std::string("123"));  // Compile error 

  printStringConst(std::string("123"));  // OK



  std::string hello( "Hello" );

  printStringNoConst(hello);  // OK

  printStringConst(hello);  // OK



  return 0;

}







$ g++ -o test test.cpp



test.cpp: In function ‘int main()’:

test.cpp:19:27: error: cannot bind non-const lvalue reference of type ‘std::string&’ {aka ‘std::__cxx11::basic_string<char>&’} to an rvalue of type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}

   19 |   printStringNoConst(std::string("123"));

      |                           ^~~~~~~~~~~~~

test.cpp:5:39: note:   initializing argument 1 of ‘void printStringNoConst(std::string&)’

    5 | void printStringNoConst(std::string & s)

参考:

Copy constructors - cppreference.comhttps://en.cppreference.com/w/cpp/language/copy_constructor

Copy constructors, assignment operators, - C++ Articleshttp://www.cplusplus.com/articles/y8hv0pDG/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值