首页IT科技拷贝构造函数原理(C++:拷贝构造函数)

拷贝构造函数原理(C++:拷贝构造函数)

时间2025-06-14 00:58:14分类IT科技浏览3956
导读:1. 拷贝和拷贝构造函数 拷贝和复制是一个意思,对应的英文单词都是copy。对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建...

1. 拷贝和拷贝构造函数

拷贝和复制是一个意思            ,对应的英文单词都是copy            。对于计算机来说                  ,拷贝是指用一份原有的            、已经存在的数据创建出一份新的数据      ,最终的结果是多了一份相同的数据                  。例如         ,将 Word 文档拷贝到U盘去复印店打印                  ,将 D 盘的图片拷贝到桌面以方便浏览         ,将重要的文件上传到百度网盘以防止丢失等      ,都是「创建一份新数据」的意思      。

在 C++ 中                  ,拷贝并没有脱离它本来的含义            ,只是将这个含义进行了“特化            ”   ,是指用已经存在的对象创建出一个新的对象         。从本质上讲                  ,对象也是一份数据               ,因为它会占用内存                  。严格来说,对象的创建包括两个阶段               ,首先要分配内存空间                  ,然后再进行初始化:

分配内存很好理解   ,就是在堆区                  、栈区或者全局数据区留出足够多的字节         。这个时候的内存还比较“原始                  ”            ,没有被“教化      ”                  ,它所包含的数据一般是零值或者随机值      ,没有实际的意义      。 初始化就是首次对内存赋值         ,让它的数据有意义                  。注意是首次赋值                  ,再次赋值不叫初始化            。初始化的时候还可以为对象分配其他的资源(打开文件      、连接网络         、动态分配内存等)         ,或者提前进行一些计算(根据价格和数量计算出总价                  、根据长度和宽度计算出矩形的面积等)等   。说白了      ,初始化就是调用构造函数                  。

很明显                  ,这里所说的拷贝是在初始化阶段进行的            ,也就是用其它对象的数据来初始化新对象的内存               。那么   ,如何用拷贝的方式来初始化一个对象呢?其实这样的例子比比皆是                  ,string 类就是一个典型的例子。

#include <iostream> #include <string> using namespace std; void func(string str){ cout<<str<<endl; } int main(){ string s1 = "http://c.biancheng.net"; string s2(s1); string s3 = s1; string s4 = s1 + " " + s2; func(s1); cout<<s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl; return 0; }

运行结果:

http://c.biancheng.net http://c.biancheng.net http://c.biancheng.net http://c.biancheng.net http://c.biancheng.net http://c.biancheng.net

s1         、s2      、s3                  、s4 以及 func() 的形参 str               ,都是使用拷贝的方式来初始化的               。

对于 s1,表面上看起来是将一个字符串直接赋值给了 s1               ,实际上在内部进行了类型转换                  ,将 const char * 类型转换为 string 类型后才赋值的   ,这其实涉及到C++转换构造函数的知识                  。s4 也是类似的道理   。

对于 s1            、s2   、s3                  、s4            ,都是将其它对象的数据拷贝给当前对象                  ,以完成当前对象的初始化            。

对于 func() 的形参 str      ,其实在定义时就为它分配了内存         ,但是此时并没有初始化                  ,只有等到调用 func() 时         ,才会将其它对象的数据拷贝给 str 以完成初始化                  。

当以拷贝的方式初始化一个对象时      ,会调用一个特殊的构造函数                  ,就是拷贝构造函数(Copy Constructor)      。

下面的例子演示了拷贝构造函数的定义和使用:

#include <iostream> #include <string> using namespace std; class Student{ public: Student(string name = "", int age = 0, float score = 0.0f); //普通构造函数 Student(const Student &stu); //拷贝构造函数(声明) public: void display(); private: string m_name; int m_age; float m_score; }; Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ } //拷贝构造函数(定义) Student::Student(const Student &stu){ this->m_name = stu.m_name; this->m_age = stu.m_age; this->m_score = stu.m_score; cout<<"Copy constructor was called."<<endl; } void Student::display(){ cout<<m_name<<"的年龄是"<<m_age<<"            ,成绩是"<<m_score<<endl; } int main(){ Student stu1("小明", 16, 90.5); Student stu2 = stu1; //调用拷贝构造函数 Student stu3(stu1); //调用拷贝构造函数 stu1.display(); stu2.display(); stu3.display(); return 0; }

运行结果:

Copy constructor was called. Copy constructor was called. 小明的年龄是16   ,成绩是90.5 小明的年龄是16                  ,成绩是90.5 小明的年龄是16               ,成绩是90.5

第 8 行是拷贝构造函数的声明,第 20 行是拷贝构造函数的定义         。拷贝构造函数只有一个参数               ,它的类型是当前类的引用                  ,而且一般都是 const 引用                  。

1.1 为什么必须是当前类的引用呢?

如果拷贝构造函数的参数不是当前类的引用   ,而是当前类的对象            ,那么在调用拷贝构造函数时                  ,会将另外一个对象直接传递给形参      ,这本身就是一次拷贝         ,会再次调用拷贝构造函数                  ,然后又将一个对象直接传递给了形参         ,将继续调用拷贝构造函数……这个过程会一直持续下去      ,没有尽头                  ,陷入死循环         。

只有当参数是当前类的引用时            ,才不会导致再次调用拷贝构造函数   ,这不仅是逻辑上的要求                  ,也是 C++ 语法的要求      。

1.2 为什么是 const 引用呢?

拷贝构造函数的目的是用其它对象的数据来初始化当前对象               ,并没有期望更改其它对象的数据,添加 const 限制后               ,这个含义更加明确了                  。

另外一个原因是                  ,添加 const 限制后   ,可以将 const 对象和非 const 对象传递给形参了            ,因为非 const 类型可以转换为 const 类型            。如果没有 const 限制                  ,就不能将 const 对象传递给形参      ,因为 const 类型不能转换为非 const 类型         ,这就意味着                  ,不能使用 const 对象来初始化当前对象了   。

以上面的 Student 类为例         ,将 const 去掉后      ,拷贝构造函数的原型变为:

Student::Student(Student &stu);

此时                  ,下面的代码就会发生错误:

const Student stu1("小明", 16, 90.5); Student stu2 = stu1; Student stu3(stu1);

stu1 是 const 类型            ,在初始化 stu2               、stu3 时   ,编译器希望调用Student::Student(const Student &stu)                  ,但是这个函数却不存在               ,又不能将 const Student 类型转换为 Student 类型去调用Student::Student(Student &stu),所以最终调用失败了                  。

当然               ,也可以再添加一个参数为 const 引用的拷贝构造函数                  ,这样就不会出错了               。换句话说   ,一个类可以同时存在两个拷贝构造函数            ,一个函数的参数为 const 引用                  ,另一个函数的参数为非 const 引用。

2. 默认拷贝构造函数

其实      ,即使我们没学习过拷贝构造函数         ,实际上却已经在使用拷贝的方式创建对象了                  ,并且也没有引发什么错误               。这是因为         ,如果程序员没有显式地定义拷贝构造函数      ,那么编译器会自动生成一个默认的拷贝构造函数                  。这个默认的拷贝构造函数很简单                  ,就是使用“老对象         ”的成员变量对“新对象                  ”的成员变量进行一一赋值            ,和上面 Student 类的拷贝构造函数非常类似   。

对于简单的类   ,默认拷贝构造函数一般是够用的                  ,我们也没有必要再显式地定义一个功能类似的拷贝构造函数            。但是当类持有其它资源时               ,如动态分配的内存、打开的文件               、指向其他数据的指针                  、网络连接等,默认拷贝构造函数就不能拷贝这些资源               ,我们必须显式地定义拷贝构造函数                  ,以完整地拷贝对象的所有数据                  。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
小米路由器上的硬盘数据怎么打开(小米MINI路由器的硬盘的详细教程) 移动端的html5网页制作(移动端H5网页开发必备知识)