C语言日记 35 拷贝构造函数
创始人
2024-04-08 13:01:08

书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语句的时候,无论我们有多少个返回值,最终只输出第一个返回值(输出只输出第一个返回值,返回是不是只有第一个返回值(那)我就不知道了)

相关内容

热门资讯

最新或2023(历届)我向国旗... 第一篇:我向国旗敬礼没有任何标志,能像我们的国旗那样凝结了华夏子孙对祖国的热爱;没有任何一种笔,能像...
最新或2023(历届)我心中的... 第一篇:我心中的长征精神今天,老师给我们上了《金色的鱼钩》这一课,我感慨万千。这篇课文讲的是红四方面...
最新或2023(历届)关于人物... 第一篇:表姐二怪远在他乡的表姐与我并无血缘关系,她只是我的干表姐而已。她的大名叫郑xx。她有许多漂亮...
最新或2023(历届)己所不欲... ,因岁月的冲洗,显得枯黄陈旧,将历史的足迹点点抹淡。然而,华夏五千年的文化,总有些像《论语》一类的书...
最新或2023(历届)以感动为... 也许,感动是夏日一缕凉爽的清风,是冬日里一轮暖暖的太阳,是沁人心脾的甘泉,是芳香四溢的一杯热茶……拥...