C++拷贝构造函数(复制构造函数)
- 格式:docx
- 大小:21.49 KB
- 文档页数:7
一、单选题1.每个类()构造函数A. 只能有一个B. 只可有私有的C. 可以有多个D. 只可有缺省的2.已知类A中的一个成员函数的说明如下:void Set(A &a);则该函数的参数“A &a”的含义是( )A. 指向A的指针为aB. 将变量a的地址赋给类AC. 类A对象引用a用作函数的形参D. 变量A与a按位与后作函数参数3.假定AB为一个类,则执行AB x;语句时将自动调用该类的( )A. 有参构造函数B. 无参构造函数C. 拷贝构造函数D. 赋值构造函数4.拷贝(复制)构造函数的作用是A. 进行数据类型的转换B. 用对象调用成员函数C. 用对象初始化对象D. 用一般类型的数据初始化对象5.下列关于析构函数的描述中,错误的是( )A. 类中有且仅有一个析构函数B. 析构函数可以有形参C. 析构函数没有函数类型D. 析构函数在对象消失时被自动执行6.下列对类的构造函数和析构函数的描述中,正确的是( )A. 构造函数可以重载,析构函数不能B. 构造函数不能重载,析构函数可以C. 构造函数可以重载,析构函数也可以重载D. 构造函数不能重载,析构函数也不能重载7.假定MyClass为一个类,那么下列的函数说明中,( )为该类的析构函数.A. void ~MyClass();B. ~MyClass(int n);C. MyClass();D. ~MyClass();8.为了使类中的成员不能被类的对象通过成员操作符访问,则不能把该成员的访问权限定义为A. publicB. protectedC. privateD. static9.下列对与拷贝构造函数的描述中,正确的( )A. 在C++中,如果不自定义类的拷贝构造函数,则每个类都有默认的拷贝构造函数B. 必须为每个类定义拷贝初始化构造函数C. 如果要使用拷贝构造函数,则必须在类中显示进行定义D. 定义拷贝构造函数则无须定义构造函数10.对于下面定义的类MyClass,在函数f()中将对象成员n的值修改为50的语句(划线处)应该是( )class MyClass{public:MyClass(int x){n=x;}void SetValue(int n1){n=n1;}private:int n;};int f(){MyClass *ptr=new MyClass(45);__________;}A. MyClass(50)B. SetValue(50)C. ptr - > SetValue(50)D. ptr - > n=50二、填空题1. ______运算符对指定类型对象动态分配内存并返回该类型的指针。
详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现C++中⼀般创建对象,拷贝或赋值的⽅式有构造函数,拷贝构造函数,赋值函数这三种⽅法。
下⾯就详细⽐较下三者之间的区别以及它们的具体实现1.构造函数构造函数是⼀种特殊的类成员函数,是当创建⼀个类的对象时,它被调⽤来对类的数据成员进⾏初始化和分配内存。
(构造函数的命名必须和类名完全相同)⾸先说⼀下⼀个C++的空类,编译器会加⼊哪些默认的成员函数默认构造函数和拷贝构造函数析构函数赋值函数(赋值运算符)取值函数**即使程序没定义任何成员,编译器也会插⼊以上的函数!注意:构造函数可以被重载,可以多个,可以带参数;析构函数只有⼀个,不能被重载,不带参数⽽默认构造函数没有参数,它什么也不做。
当没有重载⽆参构造函数时,A a就是通过默认构造函数来创建⼀个对象下⾯代码为构造函数重载的实现<span style="font-size:14px;">class A{int m_i;Public:A(){Cout<<”⽆参构造函数”<<endl;}A(int i):m_i(i) {} //初始化列表}</span>2.拷贝构造函数拷贝构造函数是C++独有的,它是⼀种特殊的构造函数,⽤基于同⼀类的⼀个对象构造和初始化另⼀个对象。
当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建⼀个对象A a;A b(a);A b=a; 都是拷贝构造函数来创建对象b强调:这⾥b对象是不存在的,是⽤a 对象来构造和初始化b的!!先说下什么时候拷贝构造函数会被调⽤:在C++中,3种对象需要复制,此时拷贝构造函数会被调⽤1. 1)⼀个对象以值传递的⽅式传⼊函数体2. 2)⼀个对象以值传递的⽅式从函数返回3. 3)⼀个对象需要通过另⼀个对象进⾏初始化什么时候编译器会⽣成默认的拷贝构造函数:1. 1)如果⽤户没有⾃定义拷贝构造函数,并且在代码中使⽤到了拷贝构造函数,编译器就会⽣成默认的拷贝构造函数。
c++ string类的常用方法一、C++ string类的常用方法1、string类的构造函数string() // 构造空串string(const char* s) // 把null结尾的字符串s拷贝到字符串中string(const string& str) // 拷贝构造函数,复制str到此串string(char c, int n) // 用n个字符c构造串string(const char* s, int n) // 拷贝字符数组中前n个字符2、string类的成员函数2.1 长度控制函数int size() const; // 返回字符串的长度int length() const; // 返回字符串的长度,等价于size()void resize(int n, char c); // 改变字符串长度,如果n 大于原来的长度,用字符c来填充2.2 内容操作函数string& operator=(const char* s); // 赋值,把s的内容复制到字符串中string& assign(const char* s); // 赋值,把s的内容复制到字符串中string& append(const char* s); // 把字符串s添加到串尾string& append(const char* s, int n); // 把s前n个字符添加到串尾string& insert(int p0, const char* s); // 在p0位置上插入字符串sstring& erase(int p0, int n); // 删除p0开始,n个字符int find(const char* s, int pos=0); // 在pos之后查找子串s,返回子串s在原串中的起始位置int find(char c, int pos=0); // 从pos开始查找字符c,返回字符c在原串中的位置int rfind(const char* substr,int pos=npos); // 从pos开始向前查找子串substr,返回子串substr在原串中的起始位置int rfind(char c, int pos=npos); // 从pos开始向前查找字符c,返回字符c在原串中的位置string substr(int pos, int n); // 返回串pos 位置开始,长度为n的子串2.3 字符串比较函数int compare(const char* s); // 比较原串和sint compare(int p0, int n, const char* s); // 比较串中p0开始,n个字符的子串和s2.4 数据访问函数char& operator[](int i); // 返回串中第i个字符的引用const char& operator[](int i) const; // 返回串中第i个字符的引用const char* c_str() const; // 返回字符串以null结尾的字符串2.5 输入输出函数ostream& operator<<(ostream& os, const string& str); // 输出字符串istream& operator>>(istream& is, string& str); // 输入字符串。
C++拷贝(复制)构造函数详解⼀. 什么是拷贝构造函数⾸先对于普通类型的对象来说,它们之间的复制是很简单的,例如:[c-sharp]1. int a = 100;2. int b = a;⽽类对象与普通对象不同,类对象内部结构⼀般较为复杂,存在各种成员变量。
下⾯看⼀个类对象拷贝的简单例⼦。
[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //⼀般函数13. void Show ()14. {15. cout<<a<<endl;16. }17. };18.19. int main()20. {21. CExample A(100);22. CExample B = A; //注意这⾥的对象初始化要调⽤拷贝构造函数,⽽⾮赋值23. B.Show ();24. return 0;25. }运⾏程序,屏幕输出100。
从以上代码的运⾏结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。
就类对象⽽⾔,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。
下⾯举例说明拷贝构造函数的⼯作过程。
[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //拷贝构造函数13. CExample(const CExample& C)14. {15. a = C.a;16. }17.18. //⼀般函数19. void Show ()20. {21. cout<<a<<endl;22. }23. };24.25. int main()26. {27. CExample A(100);28. CExample B = A; // CExample B(A); 也是⼀样的29. B.Show ();30. return 0;31. }CExample(const CExample& C) 就是我们⾃定义的拷贝构造函数。
结构体拷贝构造函数
结构体是C语言中一种很常用的数据类型,它可以将不同类型的数据组合在一起,为使用者提供了很多方便。
对于结构体,有时当我们想要复制一个结构体时,就需要用到结构体拷贝构造函数,它可以将一个已经存在的结构体拷贝到新结构体中。
原来的结构体在构造函数中传入,该构造函数实现的功能是使用原始结构体的信息来构造一个新的结构体。
构造函数一般有两种,一种是深拷贝(deep copy),其中,拷贝函数会完全复制原来结构体里面的每个成员;另一种是浅拷贝(shallow copy),只会复制原来结构体里面的指针类型成员,而不会拷贝里面的值,也就是说,新的结构体里的指针类型成员,都指向原来的结构体里的成员。
对于深拷贝而言,不仅会拷贝结构体本身里面的成员,还会拷贝指针类型的成员;而对于浅拷贝,只会拷贝原来结构体里面的指针类型成员,指向的内容还是原来的结构体里的内容,而不会再开辟新的内存空间。
总之,结构体拷贝构造函数可以将一个已有的结构体拷贝到一个新的结构体中,数据会以浅拷贝或者深拷贝的方式进行复制,让用户更方便地实现数据复制。
c++中拷贝构造函数,浅拷贝和深拷贝的区别,以及移动构造函数总结⼀、构造函数、浅拷贝和深拷贝在C++提供了⼀种特殊的构造函数,称为拷贝构造函数。
拷贝构造函数具有⼀般构造函数的所有特性,其作⽤是使⽤⼀个已经存在的对象(由拷贝构造函数的参数指定的对象)去初始化⼀个新的同类对象,即完成本类对象的复制。
程序员如果没有定义拷贝构造函数,系统会⾃动⽣成⼀个默认的拷贝构造函数,其功能是把已存在的每个数据成员都复制到新对象中。
程序员定义拷贝构造函数时,⼀般形式: 类名(类名 & 对象){}拷贝构造函数在三种情况下会被⾃动调⽤(1)⽤⼀个对象去初始化⼀个同类的对象 XX B(A) (2) XX aa = a,当⼀个新对象被定义的时候,即便这个时候是使⽤了'='运算符,它真实调⽤的是初始化函数copy constructor,⽽不是调⽤copy assignment operator去进⾏赋值操作。
;(3)如果函数的形参是对象,进⾏形参和实参结合时,调⽤拷贝构造函数。
f(A);(4)如果函数的返回值是对象,返回主调函数时,调⽤拷贝构造函数。
XX B=g();其中默认的拷贝构造函数是浅拷贝。
当类的数据成员有指针类型是,假设同类对象A初始化B,A和B对象使⽤同⼀内存区域。
在撤销对象时,导致对这⼀内存的两次释放,也就是说浅层复制:只复制指向对象的指针,⽽不复制引⽤对象本⾝。
这时候要求程序员编制拷贝构造函数,使对象B的指针指向另外的内存区域,这叫深拷贝,深层复制:复制引⽤对象本⾝。
⼆、移动构造函数右值引⽤,临时值,如果是临时值,不调⽤深拷贝,⽽是移动构造函数(move construct),来提升性能。
#include<iostream>using namespace std;class Test{public:Test() :x(0){cout << "构造函数 this = " << this << endl;}Test(int x) :x(x){cout << "构造函数 this = " << this << endl;}Test(const Test& another) :x(another.x){cout << "拷贝构造函数 this = " << this << " from " << &another << endl;}Test(const Test&& another) : x(another.x){cout << "移动构造函数 this = " << this<< " from " << &another << endl;}~Test(){cout << "析构函数 this = " << this << endl;}friend ostream& operator<<(ostream& out, const Test &t);private:int x;};//运算符重载ostream& operator<<(ostream& out, const Test &t){out << "&t = " << &t << ",x = " << t.x;return out;}Test maketest(){Test x(4);cout << "hell" << endl;return x;}int main(){//测试拷贝构造函数Test test(3);cout << test << endl;Test test2(test);cout << test2 << endl;cout << endl;//测试移动构造函数Test first = maketest();//有⼀个临时对象,cout << first << endl;cout << endl;system("pause");}//测试移动构造函数1Test first = maketest();//有⼀个临时对象,/*测试1结果分析:正常情况下直接调⽤拷贝构造函数,如果定义了移动构造函数,则直接调⽤移动构造函数。
C++中构造函数作⽤⼀、 构造函数是⼲什么的该类对象被创建时,编译系统对象分配内存空间,并⾃动调⽤该构造函数->由构造函数完成成员的初始化⼯作eg: Counter c1;编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调⽤构造函数Counter( )⾃动地初始化对象c1的m_value值设置为0故:构造函数的作⽤:初始化对象的数据成员。
⼆、 构造函数的种类class Complex{private :double m_real;double m_imag;public:// ⽆参数构造函数// 如果创建⼀个类你没有写任何构造函数,则系统会⾃动⽣成默认的⽆参构造函数,函数为空,什么都不做// 只要你写了⼀个下⾯的某⼀种构造函数,系统就不会再⾃动⽣成这样⼀个默认的构造函数,如果希望有⼀个这样的⽆参构造函数,则需要⾃⼰显⽰地写出来Complex(void){m_real = 0.0;m_imag = 0.0;}// ⼀般构造函数(也称重载构造函数)// ⼀般构造函数可以有各种参数形式,⼀个类可以有多个⼀般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)// 例如:你还可以写⼀个 Complex( int num)的构造函数出来// 创建对象时根据传⼊的参数不同调⽤不同的构造函数Complex(double real, double imag){m_real = real;m_imag = imag;}// 复制构造函数(也称为拷贝构造函数)// 复制构造函数参数为类对象本⾝的引⽤,⽤于根据⼀个已存在的对象复制出⼀个新的该类的对象,⼀般在函数中会将已存在对象的数据成员的值复制⼀份到新创建的对象中 // 若没有显⽰的写复制构造函数,则系统会默认创建⼀个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的⽂章论述Complex(const Complex & c){// 将对象c中的数据成员值复制过来m_real = c.m_real;m_imag = c.m_imag;}// 类型转换构造函数,根据⼀个指定的类型的对象创建⼀个本类的对象,//需要注意的⼀点是,这个其实就是⼀般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使⽤explicit来限制这种转换。
复制构造函数和拷贝构造函数
复制构造函数和拷贝构造函数是C++中两个重要的概念。
其中,复制构造函数指的是当一个对象被复制到另一个对象时,所调用的构造函数;而拷贝构造函数则是一个特定类型的构造函数,用于从一个已有的对象创建一个新的对象。
重要的是要理解这两个概念之间的区别。
复制构造函数是一个特殊的构造函数,用于创建一个新的对象,而拷贝构造函数则是从一个已存在的对象创建一个新的对象。
这两个函数的实现方式也有所不同,尽管它们的目的都是为了创建一个新的对象。
在C++中,有两种方式可以定义一个拷贝构造函数。
一个是使用一个已有的对象来创建一个新的对象;另一个是使用一个函数来创建一个新的对象。
无论哪种方式,拷贝构造函数的目的都是为了创建一个新的对象,以便可以对其进行操作。
在实际的程序设计中,拷贝构造函数和复制构造函数通常是必须的。
这是因为,如果没有这些函数,程序将无法正确地创建和操作对象。
因此,当编写C++程序时,需要注意这两个概念的区别,并确保正确地实现拷贝构造函数和复制构造函数。
- 1 -。
有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。
例如,将Word 文档拷贝到U盘去复印店打印,将D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建一份新数据」的意思。
在C++ 中,拷贝并没有脱离它本来的含义,只是将这个含义进行了“特化”,是指用已经存在的对象创建出一个新的对象。
从本质上讲,对象也是一份数据,因为它会占用内存。
严格来说,对象的创建包括两个阶段,首先要分配内存空间,然后再进行初始化:
∙分配内存很好理解,就是在堆区、栈区或者全局数据区留出足够多的字节。
这个时候的内存还比较“原始”,没有被“教化”,它所包含的数据一般是零值或者随机值,没有实际的意义。
∙初始化就是首次对内存赋值,让它的数据有意义。
注意是首次赋值,再次赋值不叫初始化。
初始化的时候还可以为对象分配其他的资源(打开文件、连接网络、动态分配内存等),或者提前进行一些计算(根据价格和数量计算出总价、根据长度和宽度计算出矩形的面积等)等。
说白了,初始化就是调用构造函数。
很明显,这里所说的拷贝是在初始化阶段进行的,也就是用其它对象的数据来初始化新对象的内存。
那么,如何用拷贝的方式来初始化一个对象呢?其实这样的例子比比皆是,string 类就是一个典型的例子。
1.#include<iostream>
2.#include<string>
ing namespace std;
4.
5.void func(string str){
6.cout<<str<<endl;
7.}
8.
9.int main(){
10.string s1 ="";
11.string s2(s1);
12.string s3 = s1;
13.string s4 = s1 +" "+ s2;
14.func(s1);
15.cout<<s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl;
16.
17.return0;
18.}
运行结果:
s1、s2、s3、s4 以及func() 的形参str,都是使用拷贝的方式来初始化的。
对于s1,表面上看起来是将一个字符串直接赋值给了s1,实际上在内部
进行了类型转换,将const char * 类型转换为string 类型后才赋值的,
这点我们将在《C++转换构造函数》一节中详细讲解。
s4 也是类似的道
理。
对于s1、s2、s3、s4,都是将其它对象的数据拷贝给当前对象,以完成当前对象的初始化。
对于func() 的形参str,其实在定义时就为它分配了内存,但是此时并没有初始化,只有等到调用func() 时,才会将其它对象的数据拷贝给str 以完成初始化。
当以拷贝的方式初始化一个对象时,会调用一个特殊的构造函数,就是拷贝构造函数(Copy Constructor)。
下面的例子演示了拷贝构造函数的定义和使用:
1.#include<iostream>
2.#include<string>
ing namespace std;
4.
5.class Student{
6.public:
7.Student(string name ="",int age =0,float score =0.0f);//普通构造函数
8.Student(const Student &stu);//拷贝构造函数(声明)
9.public:
10.void display();
11.private:
12.string m_name;
13.int m_age;
14.float m_score;
15.};
16.
17.Student::Student(string name,int age,float score):m_name(name),m_age(age),
m_score(score){}
18.
19.//拷贝构造函数(定义)
20.Student::Student(const Student &stu){
21.this->m_name = stu.m_name;
22.this->m_age = stu.m_age;
23.this->m_score = stu.m_score;
24.
25.cout<<"Copy constructor was called."<<endl;
26.}
27.
28.void Student::display(){
29.cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
30.}
31.
32.int main(){
33.Student stu1("小明",16,90.5);
34.Student stu2 = stu1;//调用拷贝构造函数
35.Student stu3(stu1);//调用拷贝构造函数
36.stu1.display();
37.stu2.display();
38.stu3.display();
39.
40.return0;
41.}
运行结果:
Copy constructor was called.
Copy constructor was called.
小明的年龄是16,成绩是90.5
小明的年龄是16,成绩是90.5
小明的年龄是16,成绩是90.5
第8 行是拷贝构造函数的声明,第20 行是拷贝构造函数的定义。
拷贝构造函数只有一个参数,它的类型是当前类的引用,而且一般都是const 引用。
1) 为什么必须是当前类的引用呢?
如果拷贝构造函数的参数不是当前类的引用,而是当前类的对象,那么在调用拷贝构造函数时,会将另外一个对象直接传递给形参,这本身就是一次拷贝,会再次调用拷贝构造函数,然后又将一个对象直接传递给了形参,将继续调用拷贝构造函数……这个过程会一直持续下去,没有尽头,陷入死循环。
只有当参数是当前类的引用时,才不会导致再次调用拷贝构造函数,这不仅是逻辑上的要求,也是C++ 语法的要求。
2) 为什么是const 引用呢?
拷贝构造函数的目的是用其它对象的数据来初始化当前对象,并没有期望更改其它对象的数据,添加const 限制后,这个含义更加明确了。
另外一个原因是,添加const 限制后,可以将const 对象和非const 对象传递给形参了,因为非const 类型可以转换为const 类型。
如果没有const 限制,就不能将const 对象传递给形参,因为const 类型不能转换为非const 类型,这就意味着,不能使用const
对象来初始化当前对象了。
以上面的Student 类为例,将const 去掉后,拷贝构造函数的原型变为:
此时,下面的代码就会发生错误:
1.const Student stu1("小明",16,90.5);
2.Student stu2 = stu1;
3.Student stu3(stu1);
当然,你也可以再添加一个参数为const 引用的拷贝构造函数,这样就不会出错了。
换句话说,一个类可以同时存在两个拷贝构造函数,一个函数的参数为const 引用,另一个函数的参数为非const 引用。
默认拷贝构造函数
在前面的教程中,我们还没有讲解拷贝构造函数,但是却已经在使用拷贝的方式创建对象了,并且也没有引发什么错误。
这是因为,如果程序员没有显式地定义拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。
这个默认的拷贝构造函数很简单,就是使用“老对象”的成员变量对“新对象”的成员变量进行一一赋值,和上面Student 类的拷贝构造函数非常类似。
对于简单的类,默认拷贝构造函数一般是够用的,我们也没有必要再显式地定义一个功能类似的拷贝构造函数。
但是当类持有其它资源时,如动态分配的内存、打开的文件、指向其他数据的指针、网络连接等,默认拷贝构造函数就不能拷贝这些资源,我们必须显式地定义拷贝构造函数,以完整地拷贝对象的所有数据,这点我们将在《C++深拷贝和浅拷贝》一节中深入讲解。