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

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

时间2025-08-01 17:08:23分类IT科技浏览4543
导读: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
vuejs生命周期函数(VUE生命周期函数) 云服务器网络安全防护措施(云服务器防火墙设置的方法是什么)