3-5拷贝构造函数
Time myTime;
Time myTime2 = myTime;
Time myTime3 ( myTime );
Time myTime4 { myTime };
Time myTime5 = { myTime };
默认情况下,类对象的拷贝时每个成员变量的逐个拷贝。
如果一个类的构造函数的第一个参数是所属的类类型的引用,如果还有其他额外参数,那么这些额外的参数还都有默认值,则这个构造函数称为拷贝构造函数。
函数默认参数必须放在函数声明中,除非该函数没有函数声明。
(拷贝构造函数除第一个类引用外,如果后面还有其他的参数则必须有默认值)
Time::Time(const Time &tmp, int a = 10)
(1)建议拷贝构造函数第一个参数总是带着const
(2)explicit:拷贝构造函数一般不要声明为explicit
成员函数逐个拷贝的功能因为我们自己定义的拷贝构造函数的存在而丢失了作用。
或者说我们自己的拷贝函数取代了系统默认的每个变量逐个拷贝的行为。
//Time.h
class Time{
public:
int Hour{ 0 };
int Minute{ 0 };
const int C_Second{ 0 };
Time(Time &tmpTime, int a = 1);
};
//Time.cpp
Time::Time(const Time &tmpTime, int a = 1){
}
//file.cpp
#include "Time.h"
int main(){
Time mytime;
Time myTime2 = myTime; //调用拷贝构造函数
Time myTime3 ( myTime ); //调用拷贝构造函数
Time myTime4 { myTime }; //调用拷贝构造函数
Time myTime5 = { myTime }; //调用拷贝构造函数
Time myTime6;
myTime6 = mytime; //没有调用拷贝构造函数
}
(a)如果我们没有定义一个拷贝构造函数,编译器会为我们定义一个;
(b)如果是编译器给我们合成的拷贝构造函数,这个拷贝构造函数一般也是将参数tmpTime(类对象)的成员逐个拷贝到正在创建的对象中。
每个成员的类型决定了它如何拷贝,比如说成员变量是整型的,那么直接把值拷贝过来;
如果成员变量是类类型,那么会调用这个类的拷贝构造函数来拷贝。
(c)如果自己定义了拷贝构造函数,那么就取代了系统合成的拷贝构造函数,这个时候,你就必须在自己的拷贝构造函数中给类成员变量赋值,以免出现类成员没有被赋值就使用的情况发生。
还有一些情况会发生调用拷贝构造函数的情况:
(1)将一个对象作为实际参数传给一个非引用类型的参数。
(2)从一个函数中返回一个对象的时候。
void func(Time tmp){
return;
}
Time mytime;
func(mytime);
//--------------------------------------------------
Time func(){
Time tmpTime;
return tmpTime;//系统产生了临时对象
}
Time time = func();
c++拷贝构造函数详解
一、什么是拷贝构造函数
首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=100;
int b=a
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。
#include<iostream>
using namespace std;
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a=b;
printf("constructor is called\n");
}
//拷贝构造函数
CExample(const CExample & c)
{
a=c.a;
printf("copy constructor is called\n");
}
//析构函数
~CExample()
{
cout<<"destructor is called\n";
}
void Show()
{
cout<<a<<endl;
}
};
int main()
{
CExample A(100);
CExample B=A;
B.Show();
return 0;
}
运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。
CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
二、拷贝构造函数的调用时机
1. 当函数的参数为类的对象时
#include<iostream>
using namespace std;
class CExample
{
private:
int a;
public:
CExample(int b)
{
a=b;
printf("constructor is called\n");
}
CExample(const CExample & c)
{
a=c.a;
printf("copy constructor is called\n");
}
~CExample()
{
cout<<"destructor is called\n";
}
void Show()
{
cout<<a<<endl;
}
};
void g_fun(CExample c)
{
cout<<"g_func"<<endl;
}
int main()
{
CExample A(100);
CExample B=A;
B.Show();
g_fun(A);
return 0;
}
2. 函数的返回值是类的对象
#include<iostream>
using namespace std;
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a=b;
printf("constructor is called\n");
}
//拷贝构造函数
CExample(const CExample & c)
{
a=c.a;
printf("copy constructor is called\n");
}
//析构函数
~CExample()
{
cout<<"destructor is called\n";
}
void Show()
{
cout<<a<<endl;
}
};
CExample g_fun()
{
CExample temp(0);
return temp;
}
int main()
{
g_fun();
return 0;
}
当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_fun()执行完后再析构掉XXXX对象。
3. 对象需要通过另外一个对象进行初始化
CExample A(100);
CExample B=A;