[C++ 从入门到精通] 11.拷贝构造函数

简介: [C++ 从入门到精通] 11.拷贝构造函数

一. 定义

拷贝构造函数:如果一个类构造区数的第一个参数是所属的类类型的引用。如果后面还有其他额外参数,并且后面这些额外的参数还都有默认值,则这个构造函数就叫拷贝构造函数。

注意拷贝构造函数默认参数必须放在函数声明中,除非该函数没有函数声明。

建议:

  1. 拷贝构造函数第一个参数总是带着const,作用参考前面的文章:传送门
  2. explicit:拷贝构造函数一般不要声明成explicit(禁止隐式转换),作用参考前面的文章:传送门
class Time
{
public:
  Time();
  int Hour;
  int Minute;
  int Second;
  //拷贝构造函数(在类对象赋值的时候,给对象一些值),
  Time(const Time &tmptime,int a = 5);
};
Time::Time():Hour(11), Minute(58), Second(49)
{
  std::cout << "调用了Time::Time()构造区数" << std::endl;
}
Time::Time(const Time &tmptime, int a):Hour(12),Minute(59), Second(50)
{
  std::cout << "调用了Time::Time(Time &taptime, int a)拷贝构造区数" << std::endl;
}

二. 调用场景

作用:会在一定的时机(定义类对象的同时被赋值),被系统默认调用。

如:

int main()
{
  Time myTime;               //调用默认构造函数(不带参数)
  Time myTime2 = myTime;     //调用拷贝构造函数
  Time myTime3(myTime);      //调用拷贝构造函数
  Time myTime4{ myTime };    //调用拷贝构造函数
  Time myTime5 = { myTime }; //调用拷贝构造函数
  Time myTime6;              //调用默认构造函数(不带参数)
  myTime6 = myTime5;         //从运行结果来看,没有调用拷贝构造函数,后面在拓展,这里暂不考虑
    system("pause");
  return 0;
}

此外,还有其他调用拷贝构造函数的情景:

1、将一个对象作为实参传递给一个非引用类型的形参。

void func(Time tmptime)
{
  std::cout << "调用了Time::Time(Time &taptime, int a)拷贝构造区数" << std::endl;
}
int main()
{
  Time myTime;
  func(myTime); 
}

运行结果:

2、从一个函数中返回一个对象的时候。

Time func()
{
  Time tmpTime;
  return tmpTime;  //系统产生了临时对象并且调用了类的拷贝构造函数
}
int main()
{
  func();  //等价于 Time myTime = func();
}

运行结果:

3、还有一些其他调用拷贝构造函数的情况以后补充。


三. 其他特性

我们前面说过,默认情况下,类对象的拷贝是每个成员变量的逐个拷贝。

成员变量逐个拷贝的功能因为定义的拷贝构造函数的存在而丢失了作用,或者说我们自己定义的拷贝构造函数取代了系统默认的每个成员变量逐个拷贝的这种行为。

a) 如果我们没有为类定义拷贝构造函数,编译器就会帮我们定义一个“合成拷贝构造函数”。

b) 编译器帮我们定义的“合成拷贝构造函数”一般也是将类对象tmptime的成员逐个拷贝到正在创建的对象中(Time myTime2 = myTime)。

每个成员的类型决定它如何拷贝:

  • 若是整型的,那么就直接把值拷贝过来赋值给正在创建的类对象;
  • 若是类类型的,那么就调用类类型的这个类的拷贝构造函数来拷贝赋值给正在创建的类对象(若自己定义了类A的拷贝构造函数,则不会调用在类A声明的类B的拷贝构造函数,只调用类B构造函数);

c) 如果自己定义了拷贝构造函数,那么系统就不会在定义“合成拷贝构造函数”,这时必须在自己定义的拷贝构造函数给类成员赋值,以免出现类成员没有被赋值就使用的情况发生。

假设我们在类成员函数中,对成员变量进行赋初值,而在拷贝构造函数中,不对成员变量进行赋初值。

Time::Time():Hour(11), Minute(58), Second(49)
{
  std::cout << "调用了Time::Time()构造区数" << std::endl;
}
Time::Time(const Time &tmptime, int a)
{
  std::cout << "调用了Time::Time(Time &taptime, int a)拷贝构造区数" << std::endl;
}

运行结果如下:所以必须在自己定义的拷贝构造函数给类成员赋值


态度"ABC"理论的三个维度,即情感(Affective)、行为(Behavioral)和认知(Cognitive)



目录
相关文章
|
3月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
52 2
C++入门12——详解多态1
|
3月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
91 1
|
3月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
32 0
|
3月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
39 0
|
3月前
|
分布式计算 Java 编译器
【C++入门(下)】—— 我与C++的不解之缘(二)
【C++入门(下)】—— 我与C++的不解之缘(二)
|
3月前
|
编译器 Linux C语言
【C++入门(上)】—— 我与C++的不解之缘(一)
【C++入门(上)】—— 我与C++的不解之缘(一)
|
3月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
42 0
|
3月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
47 0
|
3月前
|
存储 算法 C++
C++入门10——stack与queue的使用
C++入门10——stack与queue的使用
48 0
|
3月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
23 0