C语言日记 35 拷贝构造函数
创始人
2024-01-22 00:09:39
0

书P132:

拷贝构造函数的作用是

用已存在的对象初始化另一对象,两对象类类型应一样

在这里我们可以看到,

他对被拷贝的对象的要求只有“已存在的对象,两对象类类型一样”,也就是说他这里也没有说我们不能跨区域(类)拷贝

那我们可不可以在不同的类中拷贝别的对象呢?(例如在A类里面拷贝B类的某一对象)

附:

提出这个问题的原因是因为这段话下面马上就有一句话“拷贝构造函数的形参是本类对象的引用”

那么我想他这么写是不是已经在明示暗示我们不可以在不同的类中拷贝别的对象了呢?

书P132拷贝构造函数举例:

类部分(类名和类体):

class Point
{
public:Point(int xx = 0, int yy = 0)//内联构造函数{X = xx; Y = yy;}Point(Point& p);//拷贝构造函数int GetX() { return X; }int GetY() { return Y; }
private:int X, Y;
}; 

类外(构造函数定义实现部分):

Point::Point(Point& p)
{X = p.X;Y = p.Y;cout << "拷贝构造函数被调用" << endl;
}

如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

其中这里的cout语句和原内联构造函数不一样(原函数里面没有),其目的在于:

这样后面每次当我们的程序里面调用了拷贝构造函数时,每调用一次,我们就可以看到结果输出一次这个语句,而且我们还可以具体的看到具体的语句输出在哪里(什么时候被输出即拷贝构造函数在什么时候被调用)

合并并补充完整:

#include
using namespace std;
class Point
{
public:Point(int xx = 0, int yy = 0)//内联构造函数{X = xx; Y = yy;}Point(Point& p);//拷贝构造函数int Getx() { return X; }int GetY() { return Y; }
private:int X, Y;
}; 
Point::Point(Point& p)
{X = p.X;Y = p.Y;cout << "拷贝构造函数被调用" << endl;
}
int main()
{}

(1):(拷贝赋值,把对象A的值拷贝赋给对象B)

int main()
{Point A(6, 8);	//自动调用构造函数Point B(A);	//自动调用拷贝构造函数cout << B.GetX() << endl;return 0;
}

完整:(后面同理,不再赘述)

#include
using namespace std;
class Point
{
public:Point(int xx = 0, int yy = 0)//内联构造函数{X = xx; Y = yy;}Point(Point& p);//拷贝构造函数int GetX() { return X; }int GetY() { return Y; }
private:int X, Y;
}; 
Point::Point(Point& p)
{X = p.X;Y = p.Y;cout << "拷贝构造函数被调用" << endl;
}
int main()
{Point A(6, 8);	//自动调用构造函数Point B(A);	//自动调用拷贝构造函数cout << B.GetX() << endl;return 0;
}

结果:

039be7c3b07b42bfb7081cecd81cc6db.png

另外,对于验证:

如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

的说法的证明的程序如下:

#include
using namespace std;
class Point
{
public:Point(int xx = 0, int yy = 0)//内联构造函数{X = xx; Y = yy;}int GetX() { return X; }int GetY() { return Y; }
private:int X, Y;
};int main()
{Point A(6, 8);	//自动调用构造函数Point B(A);	//自动调用拷贝构造函数cout << B.GetX() << endl;return 0;
}

结果:

87ccd11ab64f4fdb9d0828b0c6d530b4.png

 后面也同理,不再赘述,只标注“使用默认拷贝构造函数结果”;

(2):(把实参A的值拷贝赋给形参p)

void fun1(Point p)//形参为类对象
{cout << p.GetX() << ',' << p.GetY() << endl;
}
int main()
{Point A(1, 2);fun1(A);return 0;
}

结果:

9e834481ed8d45f4a5e65e158d59fbfd.png

使用默认拷贝构造函数结果:

bab130006a6645808a200869368c5390.png


(3):返回值为类对象,系统自动把返回值拷贝到一个临时的无名对象中(详见书P133)

Point fun2()
{Point A(1, 2);return A;
}
int main()
{Point B;B = fun2();return 0;
}

结果:

8031f5117a21484da8e8e7a0daf34f24.png

使用默认拷贝构造函数结果:
4f68a0dcd6314ff498ca5a69ebd19033.png

例8-11 浅拷贝异常案例。

源程序:

#include
using namespace std;
class Rect
{
private:int width;int height;int* p;//指针成员
public:Rect()//构造函数,p指向堆中分配的空间{p = new int(100);cout << "calling copy constructor!" << endl;}~Rect()	//析构函数,释放动态分配的空间{cout << "destructor is called!" << endl;delete p;}
};int main()
{Rect rect1;Rect rect2(rect1);//复制对象return 0;
}

运行结果:

calling copy constructor!

destructor is called!

destructor is called!

539cd7a561b14d5fa724aee1bf7854f3.png

 3f81458258b64502b27a0e326d4ca3f0.png

像(在)这里开始,他才真正开始默认我们已经懂得和学过(默认)拷贝构造函数,不用在程序里面具体去写一遍默认拷贝构造函数的函数声明和函数体,全部用默认(隐式)拷贝构造函数;

而如果我们还没学过拷贝构造函数,由于前面构造函数的函数体看起来和往常根本没有任何区别,而后面他一下子就开始用起拷贝构造函数,不懂的话自然看着就懵了

另外,我们自己在判断程序的时候,不妨平时就把构造函数与拷贝构造函数捆绑在一起作为一个整体,简单的说,就是看到一个构造函数自动知道在其旁边就有着对应的拷贝构造函数

例8-12 深拷贝的使用。

源程序:

#include
using namespace std;
class Rect
{
private:int width;int height;int* p;	//指针成员
public:Rect()//构造函数,p指向堆中分配的空间{p = new int(100);cout << "calling copy constructor!" << endl;}Rect(const Rect& r){width = r.width;height = r.height;p = new int;//为新对象重新动态分配空间*p = *(r.p);}~Rect()//析构函数,释放动态分配的空间{cout << "destructor is called!" << endl;delete p;} 
};
//
int main()
{Rect rect1;Rect rect2(rect1);//复制对象return 0;
}

结果:

4f63bfac46944458961606eb9b1cfa4a.png

而这里这个程序和前一个示例的区别,(也就是深拷贝和浅拷贝的区别),(也)就是增加了一段给新的被粘贴的对象新写(创建)一个内存空间:

	Rect(const Rect& r){width = r.width;height = r.height;p = new int;//为新对象重新动态分配空间*p = *(r.p);}

 其中const(我记得前面好像写过关于这个的解释,但是具体在哪里已经找不得了)的意思和具体作用,简单来说就是保证程序段只读不写(只能够读取,不能够修改)

详细情况和解释,见

69 结构体-结构体中const使用场景_哔哩哔哩_bilibili

另外,对于其在成员函数中的作用的补充,详见:35 类和对象-对象特性-const修饰成员函数_哔哩哔哩_bilibili

另外,如果要写得更简单简洁,也可以这样写:

	Rect(const Rect& r){width = r.width;height = r.height;p = new int(*r.p);//p = (r.p);//编译器实际上默认实现的就是这个代码}

该段深拷贝浅拷贝的具体内容,详见:

27 类和对象-对象特性-深拷贝与浅拷贝_哔哩哔哩_bilibili

另外,为了巩固这块内容,(实际上是因为我们想达到一个深拷贝以后还能把两个对象里面的具体数据输出的效果)我们这里再把程序补充完整为处处对象具体内容同的程序:

#include
using namespace std;
class Rect
{
private:int width;int height;int* p;	//指针成员
public:Rect()//构造函数,p指向堆中分配的空间{p = new int(100);}Rect(const Rect& r){	cout << "calling copy constructor!" << endl;width = r.width;height = r.height;p = new int(*r.p);//p = (r.p);//编译器实际上默认实现的就是这个代码}~Rect()//析构函数,释放动态分配的空间{cout << "destructor is called!" << endl;delete p;}int display(){cout << "指针指向" << *p << ";   高度" << height << ";   宽度" << width << endl;return 1;}
};
//
int main()
{Rect rect1;Rect rect2(rect1);//复制对象cout << rect1.display() << endl;cout << rect2.display() << endl;return 0;
}

结果:

 改动之处:

  • 把显示输出调用拷贝构造函数的句子放到了拷贝构造函数中,而不是构造函数里面
  • 新写了display()函数,用于输出这几个对象里面的具体内容

只得一提的是:

我们原来一开始本来打算写两种形式的display()函数:

一种采用cout语句输出值,另一种采用return语句返回值

然而,当我们一开始只写cout语句的display()函数时:

	int display(){cout << "指针指向" << *p << ";   高度" << height << ";   宽度" << width << endl;}

结果:

所以在这里我们可以知道:

在类中定义的函数必须有一个返回值

但是总感觉这好像又不对吧,例如:C语言日记 32 类的对象,this指针_宇 -Yu的博客-CSDN博客

的 例8-6,这里面的

void Time::Get_Time()//定义公有成员函数
{cout << Hour << ":" << Minute << ":" << Second << endl;}

好像也没有写返回的值啊??

另外,如果我们采用return语句返回值,则display()函数为:

(1):

	int display(){return   *p ;return height;return width;}

结果:

 (2):

	int display(){return height;return   *p ;return width;}

结果:

(所以)那么这里我们就可以说,

当我们有多个返回值,return语句的时候,无论我们有多少个返回值,最终只输出第一个返回值(输出只输出第一个返回值,返回是不是只有第一个返回值(那)我就不知道了)

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...