-
2021-05-22 12:22:30
复制构造函数是构造函数的一种,也称拷贝构造函数,它只有一个参数,参数类型是本类的引用。
复制构造函数的参数可以是 const 引用,也可以是非 const 引用。 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。一个类中写两个复制构造函数,一个的参数是 const 引用,另一个的参数是非 const 引用,也是可以的。
如果类的设计者不写复制构造函数,编译器就会自动生成复制构造函数。大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制,即使得目标对象的每个成员变量都变得和源对象相等。编译器自动生成的复制构造函数称为“默认复制构造函数”。
注意,默认构造函数(即无参构造函数)不一定存在,但是复制构造函数总是会存在。
下面是一个复制构造函数的例子。
#include
using namespace std;
class Complex
{
public:
double real, imag;
Complex(double r, double i) {
real= r; imag = i;
}
};
int main(){
Complex cl(1, 2);
Complex c2 (cl); //用复制构造函数初始化c2
cout<
return 0;
}
第 13 行给出了初始化 c2 的参数,即 c1。只有编译器自动生成的那个默认复制构造函数的参数才能和 c1 匹配,因此,c2 就是以 c1 为参数,调用默认复制构造函数进行初始化的。初始化的结果是 c2 成为 c1 的复制品,即 c2 和 c1 每个成员变量的值都相等。
如果编写了复制构造函数,则默认复制构造函数就不存在了。下面是一个非默认复制构造函数的例子。
#include
using namespace std;
class Complex{
public:
double real, imag;
Complex(double r,double i){
real = r; imag = i;
}
Complex(const Complex & c){
real = c.real; imag = c.imag;
cout<
}
};
int main(){
Complex cl(1, 2);
Complex c2 (cl); //调用复制构造函数
cout<
return 0;
}
程序的输出结果是:
Copy Constructor called
1,2
第 9 行,复制构造函数的参数加不加 const 对本程序來说都一样。但加上 const 是更好的做法,这样复制构造函数才能接受常量对象作为参数,即才能以常量对象作为参数去初始化别的对象。
第 17 行,就是以 c1 为参数调用第 9 行的那个复制构造函数初始化的。该复制构造函数执行的结果是使 c2 和 c1 相等,此外还输出Copy Constructor called。
可以想象,如果将第 10 行删去或改成real = 2*c.real; imag = imag + 1;,那么 c2 的值就不会等于 c1 了。也就是说,自己编写的复制构造函数并不一定要做复制的工作(如果只做复制工作,那么使用编译器自动生成的默认复制构造函数就行了)。但从习惯上来讲,复制构造函数还是应该完成类似于复制的工作为好,在此基础上还可以根据需要做些別的操作。
构造函数不能以本类的对象作为唯一参数,以免和复制构造函数相混淆。例如,不能写如下构造函数:
Complex (Complex c) {...}
复制构造函数被调用的三种情况
复制构造函数在以下三种情况下会被调用。
1) 当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用。例如,下面的两条语句都会引发复制构造函数的调用,用以初始化 c2。
Complex c2(c1);
Complex c2 = c1;
这两条语句是等价的。
注意,第二条语句是初始化语句,不是赋值语句。赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发复制构造函数的调用。例如:
Complex c1, c2; c1 = c2 ;
c1=c2;
这条语句不会引发复制构造函数的调用,因为 c1 早已生成,已经初始化过了。
2) 如果函数 F 的参数是类 A 的对象,那么当 F 被调用时,类 A 的复制构造函数将被调用。换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的实参。
#include
using namespace std;
class A{
public:
A(){};
A(A & a){
cout<
}
};
void Func(A a){ }
int main(){
A a;
Func(a);
return 0;
}
程序的输出结果为:
Copy constructor called
这是因为 Func 函数的形参 a 在初始化时调用了复制构造函数。
前面说过,函数的形参的值等于函数调用时对应的实参,现在可以知道这不一定是正确的。如果形参是一个对象,那么形参的值是否等于实参,取决于该对象所属的类的复制构造函数是如何实现的。例如上面的例子,Func 函数的形参 a 的值在进入函数时是随机的,未必等于实参,因为复制构造函数没有做复制的工作。
以对象作为函数的形参,在函数被调用时,生成的形参要用复制构造函数初始化,这会带来时间上的开销。如果用对象的引用而不是对象作为形参,就没有这个问题了。但是以引用作为形参有一定的风险,因为这种情况下如果形参的值发生改变,实参的值也会跟着改变。
如果要确保实参的值不会改变,又希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的 const 引用。例如:
void Function(const Complex & c)
{
...
}
这样,Function 函数中出现任何有可能导致 c 的值被修改的语句,都会引发编译错误。
思考题:在上面的 Function 函数中,除了赋值语句,还有什么语句有可能改变 c 的值?例如,是否允许通过 c 调用 Complex 的成员函数?
3) 如果函数的返冋值是类 A 的对象,则函数返冋时,类 A 的复制构造函数被调用。换言之,作为函数返回值的对象是用复制构造函数初始化 的,而调用复制构造函数时的实参,就是 return 语句所返回的对象。例如下面的程序:
#include
using namespace std;
class A {
public:
int v;
A(int n) { v = n; };
A(const A & a) {
v = a.v;
cout << "Copy constructor called" << endl;
}
};
A Func() {
A a(4);
return a;
}
int main() {
cout << Func().v << endl;
return 0;
}
程序的输出结果是:
Copy constructor called
4
第19行调用了 Func 函数,其返回值是一个对象,该对象就是用复制构造函数初始化的, 而且调用复制构造函数时,实参就是第 16 行 return 语句所返回的 a。复制构造函数在第 9 行确实完成了复制的工作,所以第 19 行 Func 函数的返回值和第 14 行的 a 相等。
需要说明的是,有些编译器出于程序执行效率的考虑,编译的时候进行了优化,函数返回值对象就不用复制构造函数初始化了,这并不符合 C++ 的标准。上面的程序,用 Visual Studio 2010 编译后的输出结果如上所述,但是在 Dev C++ 4.9 中不会调用复制构造函数。把第 14 行的 a 变成全局变量,才会调用复制构造函数。对这一点,读者不必深究。
更多相关内容 -
C++中何时调用复制构造函数
2021-01-20 03:41:37在C++中,只有单个形参,而且该类型是对本类类型的引用(常用const修饰),这样的构造函数称为复制构造函数。 复制构造函数既可以自己定义又可以像默认构造函数一样被编译器隐式调用。但大多数时候,特别是类中... -
详解C++ 拷贝构造函数
2021-01-19 23:44:42拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象。 复制对象把它作为参数传递给... -
在函数中自动调用复制构造函数
2021-01-07 18:57:46在函数中自动调用复制构造函数 #include #include #include using namespace std; class A { public: A(string name):_name(name) { cout <... ,调用构造函数,构造对象地址为: <... 我是复制构造函数, 复制对象名 -
C++中拷贝构造函数的总结详解
2021-01-20 06:59:40(其实很多名字,只要静下心来想一想,就真的是顾名思义呀)拷贝又称复制,因此拷贝构造函数又称复制构造函数。百度百科上是这样说的:拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的... -
C++复制控制之复制构造函数
2021-01-20 03:40:29C++类用三个特殊的成员函数:复制构造函数、赋值操作符和析构函数 来决定类对象之间的初始化或赋值时发生什么。所谓的“复制控制”即通过这三个成员函数控制对象复制的过程。本篇文章将介绍复制构造函数。 复制... -
编译器角度看C++复制构造函数
2020-12-22 16:58:18关于复制构造函数的简单介绍,可以看我以前写过的一篇文章C++复制控制之复制构造函数该文章中介绍了复制构造函数的定义、调用时机、也对编译器合成的复制构造函数行为做了简单说明。本文因需要会涉及到上文的一些... -
C++ 构造函数中使用new时注意事项
2020-12-31 00:20:29使用new初始化对象中的指针成员时遇到的问题 在构造函数中使用new初始化指针成员,那么析构函数中就必须使delete,并且...需要自行定义一个复制构造函数和赋值构造函数,用深复制的方式把一个对象初始化给另一个对象, -
构造函数及复制构造函数
2021-04-24 15:48:57构造函数 是为了让我们自己去规定 , 我们定义对象的初始化。 #include <iostream> using std::cin; using std::cout; using std::endl; class Clock{ public: void setTime(int newS = 0, int newM = 0, ...一、构造函数
是为了让我们自己去规定 , 我们定义对象的初始化。
这里给出一段程序,作用是按格式输出 输入的时间。#include <iostream> using std::cin; using std::cout; using std::endl; class Clock{ public: void setTime(int newS = 0, int newM = 0, int newH = 0){ //这一步如果想要输出默认值,应该把主函数里的setTime函数中的参数删除。 second = newS; minute = newM; hour = newH; } void showTime(); private: int second, minute, hour; }; void Clock::showTime(){ cout << hour << ":" << minute << ":" << second <<endl; } int main(){ Clock MyClock; int second, minute, hour; cin >> second >> minute >> hour; MyClock.setTime(second, minute, hour); MyClock.showTime(); return 0; }
构造函数的性质:
● 不能定义返回值类型,也不能写return语句
●可以有形参,也可以没有形参,可以带有默认参数
●函数名必须和类名完全相同
●可以重载当我们没有写构造函数时,系统会为我们自动生成一个默认构造函数,这个默认构造函数的参数列表和内容都是空的,所以他什么都不会做!
现在 我们需要创建一个默认构造函数,让对象早初始化的时候把所有的时间全部设置为0。
//在类内定义构造函数时,不能有返回值类型,且构造函数的名字要和类一样,可以有形参也可以没有形参,这里的第一种定义是没有形参的类型: class Clock{ public: Clock(){ second = 0; minute = 0; hour = 0; } //如果不写构造函数,那么系统自动生成的构造函数 就是 Clock(){} //函数里没有任何内容,所以也不会有操作
第二种定义类型是有参数的形式:
Class Clock{ public: Clock(int newS, int newM, int newH){ second = newS; minute = newM; hour = newH; } } //或者在类外实现构造函数 Class Clock{ public: Clock(int newS, int newM, int newH); } Clock::Clock(int newS, int newM, int newH){ second = newS; minute = newM; hour = newH; } 这里在类外实现构造函数时,函数名前面仍然不要写函数的返回值类型 , 实现类外构造函数 : 在类内对函数定义,然后在类外 用 类名 :: 函数名 ( 参数 ) { 函数体 } 的形式在类外实现构造函数。 而在类外实现普通函数时,也是在类内先对函数进行定义,然后在类外 用 返回值类型 类名 :: 函数名 ( 参数 ) { 函数体 } 实现,这里需要在类 名前面加上返回值类型
调用带参数的构造函数时,是在对象创建时被调用的:
Clock MyClock2(1,2,3);
二、复制构造函数
用一个已有的对象,来执行一个新的对象的构造
●复制构造函数具有一般构造函数的所有特性,它的形参是本类的一个对象的引用,作用是用一个已经存在的对象(函数的参数)来初始化一个新的对象。
●复制构造函数的的使用方式 就是引用传参。引用就是 给一个已经存在的变量起一个别名,所以用它的变量名或者别名都可以操作该变量, 复制构造函数就是给已经存在的构造函数起一个别名。
因为:值传递就是当函数发生调用的时候,给形参分配内存空间,然后用实参的值来初始化形参,但是如果参数是一个对象,值传递“初始化形参”这个过程就会造成很多额外的时间开销,浪费系统资源,如果使用引用就不会有这样的问题。
复制构造函数能实现同类对象之间数据成员的传递。 当我们没有定义类的构造韩函数时,系统就会在必要的时候自动生成一个隐含的复制构造函数,他的作用是把初始对象的每一个数据成员的值都复制到新建的对象中,这就完成了同类对象的复制。
复制构造函数的具体声明方式 :class 类名{ public: 类名(类名& 对象名){ //实现 } };
实例:
先有一个point类,表示屏幕上的一个点——它包括两个 int 类的私有成员 x , y, 表示这个点的坐标。class Point { public: Point(Point &p); private: int x,y; }; //以下代码可实现复制构造函数 Point::Point(Point &p){ x = p.x; y = p.y; } //这里复制构造函数访问了Point类的实例对象p的两个私有成员变量 x,y 。我们需要注意的是 ———— private 与 public 的区别是对类来说的,而不是对对象来说。 拷贝构造函数是Point类的成员函数————所以它可以访问类的私有成员变量,这跟具体的对象无关。
复制构造函数被调用主要的三种情况:
●当用类的一个对象去初始化该类的另一个对象的时候:Point a(1,2); Point b(a);//用对象a初始化对象b,复制构造函数被调用 Point c = b;//用对象b给对象c赋值,复制构造函数被调用
●当函数的形参是类的对象,调用函数是进行形式结合的时候:
void f(Point p){ //函数体 } int main(){ Point a(1,2); f(a); return 0; }
●当函数返回值是类的对象,函数执行完成返回调用者的时候:
Point g(){ Point a(1,2); return a; } /*定义在函数中的变量都是局部变量,当函数返回值的时候这些局部变量 都被销毁了。同样,在函数中创建的对象也相同。 return a 这一步返 回的并不是 a 这个对象本身,而是通过复制构造函数,在主调函数中用a 重新构造的对象。在函数调用返回的时候,原来的临时对象 a 的使命已 经完成,随着整个函数中的其他临时变量一起被销毁了。*/
对于复制构造函数,我们还可以自己实现一些有选择、有变化的复制:
Point (point &p){ x = p.x+10; p=p.y; }
这个函数就会把每一个由复制构造得到的Point对象,横坐标+10.
-
C++的复制构造函数三种用法
2022-04-05 09:27:462、函数fun( )的形参是对象时,传递参数时,复制构造函数会被调用 3、函数的返回值是对象时,返回结果时,复制构造函数会被调用 接下来依次看一下这三种情况。 首先是类的定义 class node { public: int x, y; ...前言
如果真的想明白,为什么你写的函数的返回值是对象时,有的时候调用了复制构造函数,而有的时候,没有调用复制构造函数。需要明白一件事:函数的返回值是对象时,什么情况下,函数的返回是return by value,即生成了临时对象。这两个问题是等价的,因为按照道理来讲,对象作为函数的返回值时,就应该生成临时对象,调用复制构造函数,初始化这个临时对象,但是,现在的编译器,对于return by value有优化。有时候,不生产临时对象。编译器总是将栈里面的 结果对象 直接给调用者,避免了多一次的析构和构造。即使在关闭编译器优化的时候,它依然给你做了这个动作。
现在的我,还不明白具体什么情况下,生成临时对象。什么情况下,不生成临时对象。
留给以后解决
可以参考的文章参考1、参考2。三种用法
我们知道,类、struct结构体的 复制构造函数 在三种情况下会被调用,分别是:
1、创建对象a时,使用对象b去初始化对象a
2、函数fun( )的形参是对象时,传递参数时,形参 是用 复制构造函数 初始化的
3、函数的返回值是对象时,生成的临时对象,是用 复制构造函数初始化的接下来依次看一下这三种情况。
首先是类的定义
class node { public: int x, y; node () {} // 默认构造函数 node (int _x, int _y) : x(_x), y(_y) {} // 自定义的构造函数 node (const node& temp) { // 自定义的复制构造函数 x = temp.x; y = temp.y; printf("复制构造函数被调用\n"); // 打印语句,只要该复制构造函数被调用,就会有输出 } };
1、创建对象a时,使用别的对象b去初始化a
int main() { node b(8, 10); // 创建了node对象 b,并且调用了构造函数,初始化对象b node a(b); // 创建对象a, 并且使用对象b来初始化a // node a = b; // 这样也可以,只要是创建对象同时,用其余对象初始化就可以。 cout<< a.x << " " << a.y << endl; return 0; }
运行结果
复制构造函数被调用 8 10
可以看到,先输出了"复制构造函数被调用",之后又执行了打印a.x、a.y的语句。说明,在创建对象a时,使用对象b初始化对象a,是会调用复制构造函数的。调用了对象a的复制构造函数,复制构造函数的实参是对象 b。
但是下面的这种情况就不是创建对象的同时使用另一个对象初始化了。int main() { node b(8, 10); node a; // 先创建了对象 a,使用默认构造函数初始化了对象 a a = b; // 这是赋值,不是初始化 cout << a.x << " " << a.y << endl; return 0; }
运行结果
8 10
可见,首先定义一个对象a,之后再进行赋值,就不是初始化了。
2、函数fun( )的形参是对象时,传递参数时,形参 是用 复制构造函数 初始化的
void show(node temp) { // show()函数的参数是对象 cout << temp.x << " " << temp.y << endl; } int main() { node a(8, 10); // 创建了node对象 a,并且调用了构造函数,初始化数据 show(a); // 将对象a作为函数的实参传入 return 0; }
运行结果
复制构造函数被调用 8 10
可以看到,首先打印的是复制构造函数中的语句“复制构造函数被调用”,之后执行的是函数体里面的打印语句。所以,对象作为函数的参数时,传递参数时,是会调用复制构造函数的。即进行了值复制。形参temp是使用实参 a 初始化的。初始化形参temp时,调用了temp的复制构造函数,这个函数的参数是a。
更进一步,如果参数是引用:void show(node& temp) { // show()函数的参数是 对象的引用 cout << temp.x << " " << temp.y << endl; } int main() { node a(8, 10); // 创建了node对象 a,并且调用了构造函数,初始化数据 show(a); // 将对象a作为函数的实参传入 return 0; }
此时函数的参数不是对象本身,而是对象的引用。再来看一下运行结果:
8 10
如果函数的参数是 对象的引用时,传入参数的时候,没有调用复制构造函数,没有输出复制构造函数中的打印语句“复制构造函数被调用”。
所以这种情况下,就没有复制这一步,因为传入的是实参的引用。如果大量数据要作为实参传入时,最好传入的是引用。速度快。3、 函数的返回值是对象时,生成的临时对象,是用 复制构造函数 初始化的
我们知道,不同的函数,是在不同的内存空间中运行的,调用某个函数fun()时,去往另一片内存空间运行,当该函数执行结束时,这一片内存空间要被释放,其中的变量要消亡。但是,如果该函数有返回值(无论是基本类型,还是对象),则该函数要生成一个临时的值temp,用于返回给调用函数的地方。因为该函数执行结束之后,该函数里面的任何局部变量都要消失。
重点就是,该临时值temp,如果是对象的话,就是使用复制构造函数进行初始化的。node create_node() { // 函数的返回值是对象 node temp(3, 5); // 创建一个对象 return temp; // 返回对象 } int main() { node a = create_node();// a 用于接收函数返回值 cout << a.x << " " << a.y << endl; return 0; }
运行结果:
3 5
可以看到,并没有想象的那样输出“复制构造函数被调用”,也就是说明,此种情况下,并没有调用复制构造函数。
原因在于现在的编译器对于“对象作为函数的返回值”这种情况有所优化。返回值为对象时,有的情况下不再产生临时对象,因而不再调用复制构造函数。因此也就不存在临时对象被初始化。有几种情况下,还是会调用复制构造函数。
全局对象 cur 作为函数返回值,对象 a 在接收 返回值 cur 时,调用复制构造函数。#include <bits/stdc++.h> using namespace std; class node { public: int x, y; node() {} node(int _x, int _y) : x(_x), y(_y) {} node(const node& temp) { x = temp.x, y = temp.y; printf("复制构造函数被调用\n"); } }; node cur(1, 2); node fun() { // 函数的返回值是对象,并且是全局对象 return cur; } int main() { node a ; // a用于接收函数返回值 a = fun(); // a 接收函数返回值 cout << a.x << " " << a.y << endl; return 0; }
运行结果
复制构造函数被调用 1 2
可以看到,当全局对象 cur 作为函数返回值时,对象 a 在接收 返回对象 cur时,调用了复制构造函数。
详细地分析一下这个例子。fun( ) 函数执行结束后,会生成一个临时对象,假设为temp,该对象是使用对象cur进行初始化的。在这个初始化过程中,调用了temp的复制构造函数,其实参是 return 语句中的 cur。在这之后,fun( )函数彻底消失,占用的内存被释放。
之后,在main( ) 函数中的 a = fun(); 语句,该临时对象temp,被赋值给 a, 这是一个赋值语句,并不是初始化语句。该语句执行结束之后,临时对象 temp 消亡。此外,如果有一个函数,它的参数是对象,返回值也是对象,则,接收该函数的返回值时,调用了复制构造函数。
#include <bits/stdc++.h> using namespace std; class node { public: int x, y; node() {} node(int _x, int _y) : x(_x), y(_y) {} node(const node& temp) { x = temp.x, y = temp.y; printf("复制构造函数被调用\n"); } }; node fun(node& temp) { // 此处是引用,否则调用两次赋值构造函数。 return temp; } int main() { node a(1, 2), b; b = fun(a); cout << a.x << " " << a.y << endl; return 0; }
运行结果
复制构造函数被调用 1 2
可以看到,fun( ) 函数的参数是对象、返回值也是对象,此时,对象 b 接收 返回值时,就调用了复制构造函数。
总结
复制构造函数,只在对象被 初始化 时被调用。而初始化意味着某对象在创建的同时,被初始化。
具体就三种情况:
1、对象被创建时,被其余对象初始化
2、函数fun( ) 的形参是对象,则调用函数 fun()时,形参会被初始化
3、函数的返回值是 对象,则生成的临时对象会被 return 语句中的数据初始化。 -
c++复制构造函数详解
2020-07-12 16:32:41复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数,参数类型为同类对象的引用。 如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数...基本概念
复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数,参数类型为同类对象的引用。
如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数完成复制的功能。
复制构造函数的参数为同类对象的引用,可以是常应用,也可以是非常引用。形如
类名::类名(类名&)
或类名::类名(const 类名&)
默认复制构造函数
class Complex {//复数类 public: double real, imag;//分别表示实部以及虚部 public: //构造函数 Complex(double r,double i) { real = r; imag = i; } };
对于上面的复数类,假设我们有如下的初始化:
int main() { Complex c1(5, 2); cout << "c1.real=" << c1.real << "\tc1.imag=" << c1.imag << endl; Complex c2(c1);//调用复制构造函数初始化c2 cout << "c2.real=" << c2.real << "\tc2.imag=" << c2.imag << endl; return 0; }
在上面的初始化工作中,我们调用构造函数对
c1
进行初始化工作,然后输出c1
的实部以及虚部。然后用c1
去初始化c2
,此时将调用编译器自动生成的构造函数将c2
初始化为和c1
一样,然后输出c2
的实部以及虚部。运行上面的程序,得到如下的输出:c1.real=5 c1.imag=2 c2.real=5 c2.imag=2
可以看到此时
c2
被初始化为和c1
相同。编写复制构造函数
在上面的例子中,我们并没有编写复制构造函数,此时编译器将生成一个默认的复制构造函数完成复制工作。
当我们自己编写了复制构造函数之后,编译器将不再生成默认的复制构造函数。如下面的例子所示:
class Complex {//复数类 public: double real, imag;//分别表示实部以及虚部 //构造函数 Complex(double r,double i) { real = r; imag = i; } Complex(const Complex& c) {//复制构造函数 real = c.real; imag = c.imag; cout << "调用复制构造函数!!" << endl; } };
对于上面的类,在复制构造函数被调用的时候将完成复制的工作,并且向控制台输出调用复制构造函数!!,假设我们有如下的初始化:
int main() { Complex c1(5, 2); cout << "c1.real=" << c1.real << "\tc1.imag=" << c1.imag << endl; Complex c2(c1);//调用复制构造函数初始化c2 cout << "c2.real=" << c2.real << "\tc2.imag=" << c2.imag << endl; return 0; }
执行后可以得到如下的结果:
c1.real=5 c1.imag=2 调用复制构造函数!! c2.real=5 c2.imag=2
可以看到,此时我们自己编写的复制构造函数被调用,并且向屏幕输出了调用复制构造函数!!。
错误用法
复制构造函数的参数一定要是对同类对象的引用,不能为其它的。
class Complex { public: double real, imag; Complex(double r,double i) { real = r; imag = i; } Complex(Complex c) {//错误的写法 real = c.real; imag = c.imag; cout << "调用复制构造函数!!" << endl; } };
如上所示,我们错误的将参数
Complex& c
写为了Complex c
,此时我们的编译将无法通过,在visual studio 2019
中将得到以下的报错:error C2652: “Complex”: 非法的复制构造函数: 第一个参数不应是“Complex” message : 参见“Complex”的声明 error C2333: “Complex::Complex”: 函数声明中有错误;跳过函数体 error C2558: class“Complex”: 没有可用的复制构造函数或复制构造函数声明为“explicit”
总之,复制构造函数的参数一定要是同类对象的引用。
复制构造函数起作用的情况
用一个对象来初始化正在构造的对象变量
如上面编写复制构造函数小节中所展示的那样,当我们用同类的一个对象去初始化另一个对象时,会导致构造函数被调用。
需要注意的是:
Complex c1(5, 2); Complex c2(c1); Complex c3 = c1;//初始化语句,非赋值语句
上面的第三天语句为初始化语句,并非赋值语句。
函数参数作为对象传值
如果某函数有一个参数是类的对象,那么该函数被调用时,该类的复制构造函数将被调用。
class Complex {//复数类 public: Complex() {}; Complex(const Complex& c) {//复制构造函数 cout << "调用复制构造函数!!" << endl; } };
对于上面的类,假设我们有如下调用:
void fun(Complex c){} int main() { Complex c; fun(c);//调用fun(Complex c) return 0; }
其中我们定义了一个函数
fun(Complex c)
,其参数为一个Complex
对象,当我们在调用fun(Complex c)
的时候,形参c
将被初始化,此时将调用复制构造函数完成初始化工作,产生如下的输出:调用复制构造函数!!
需要注意的是,如果函数的参数为对象的引用或常引用时,将不会导致构造函数被调用,如下所示:
void fun(Complex &c){}
函数返回一个对象
如果函数的返回值是某类的对象,则函数返回时,复制构造函数将被调用。
class Complex {//复数类 public: Complex() {}; Complex(const Complex& c) {//复制构造函数 cout << "调用复制构造函数!!" << endl; } };
对于上面的类有如下调用:
Complex fun(){ Complex c(2,3); return c; } int main() { cout << fun().real << endl; return 0; }
函数
fun()
返回一个Complex
类的对象,在调用fun()
函数时会导致复制构造函数被调用。注意事项
- 上述情形未必会调用复制构造函数。因为C++标准允许编译器实现做一些优化。例如:
Class X b=X();
优化
当对象作为函数参数,在对函数进行调用时会调用复制构造函数对形参进行初始化工作,此时会产生额外的开销,我们可以将函数的参数写为对象的引用来避免额外的开销,如果担心对象的值在函数中会被改变,我们也可以用常引用的方式。
对于如下的写法:
Complex fun(Complex c){}
我们可以改写为:
Complex fun(Complex& c){} Complex fun(const Complex& c) {}
- 上述情形未必会调用复制构造函数。因为C++标准允许编译器实现做一些优化。例如:
-
[ C++ ] — 拷贝构造函数(复制构造函数)
2021-12-14 18:25:11简单来说,拷贝构造函数就是来复制对象的 如果类中没有定义拷贝构造函数,编译器会自行定义一个。 如果类成员都是简单类型(如标量值),则编译器生成的复制构造函数已足够,无需定义自己的类型。 如果类需要更复杂... -
C++——构造函数、析构函数以及复制构造函数
2021-03-09 16:28:49但是与普通变量相比,类的对象特别复杂,编译器不知如何产生代码去初始化对象,这便引出了构造函数。 1.1 构造函数是什么? C++中,构造函数是一种特殊的成员函数,在每次创建一个类的时候编译器都会默认调用构造... -
【C++】(二十一)默认构造函数和复制构造函数
2020-11-02 20:02:08文章目录默认构造函数和复制构造函数1 定义默认构造函数2 隐式类类型转换3 复制构造函数与合成复制构造函数3.1 复制构造函数3.2 合成复制构造函数4 深复制与浅复制 默认构造函数和复制构造函数 1 定义默认构造函数 ... -
C++ 函数返回值为对象时调用复制构造函数的问题
2020-02-22 15:02:50C++ 函数返回值为对象时调用复制构造函数的问题 知识点: C++中调用复制构造函数的三种情况: 通过一个对象构造另一个对象 调用参数为对象的函数 调用返回值为对象的函数 – 上述知识点在各种书籍、博客都无不同,... -
拷贝构造函数(复制构造函数)
2019-03-17 15:44:34赋值构造函数 用一个对象初始化另一个对象 特别注意:初始化操作和 等号操作是两个不同的概念 1、初始化法 Class A { public: A(int a) { m_a = a; } A(int a,int b) { m_a = a; m_b = b; } A(const A... -
【类与对象】组合类的构造函数和复制构造函数
2020-07-22 10:38:24初始化列表是用于构造函数参数的初始化,与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。 如: class myClock { ... -
复制构造函数
2021-03-18 21:50:24复制构造函数 是用来将一个对象复制到新创建的对象当中,是内容的拷贝, ...此处并未写复制构造函数,他被注释起来了 头文件声明 #ifndef __STUDENT_H__ #define __STUDENT_H__ class STUDENT { private: sta -
C++ 赋值构造函数 复制构造函数
2018-08-25 15:29:11默认构造函数 编译器提供一个不接受任何参数,也不执行任何操作的构造函数,...如果定义了构造函数,C++不会定义默认构造函数,如果希望创建对象时不显示地对他进行初始化,则必须显示的定义默认构造函数,这种默认... -
C++ 构造函数,默认构造函数,析构函数,复制构造函数
2022-03-20 00:49:35C++ 构造函数,默认构造函数,析构函数,复制构造函数 -
C++ 拷贝(复制)构造函数
2022-03-19 21:13:18拷贝构造函数用以将一个类的对象拷贝给同一个类的另一个对象,比如之前学习过的string类: string s1; string s2 = s1; 一般情况下的拷贝构造函数: class A { private: int n; double d; char s; public: ... -
c++ 复制构造函数_用C ++复制构造函数
2020-07-28 23:56:35c++ 复制构造函数 用C ++复制构造函数 (Copy Constructor in C++) Copy Constructor is a type of constructor which is used to create a copy of an already existing object of a class type. It is usually of ... -
C++ 复制构造函数
2020-03-19 12:47:07复制构造函数是一种特殊的构造函数,其形参为本类的对象引用,作用是用一个已存在的对象去初始化同类型的新对象。 class 类名{ public: 类名(形参); //构造函数 类名(const 类名 & 对象名); //复制构造... -
C++ 复制构造函数与函数返回对象
2018-08-26 12:05:05C++ 复制构造函数与函数返回对象 函数返回内部局部对象,例如 A fun1() { A a(20); // 调用自定义构造函数,构造a对象 return a; } 函数调用时,自动调用复制构造函数,赋值给左端对象.例如: // 调用... -
C++ 之构造函数(拷贝/复制构造函数)
2020-06-26 12:52:51拷贝构造函数(复制构造函数) 一、拷贝构造函数概念 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象... -
【C++】构造函数 利用构造函数对类对象进行初始化
2022-01-25 16:01:26有关构造函数与析构函数的知识点 -
C++中类的构造函数与复制构造函数
2017-04-27 10:52:42当程序运行时,编译器发现复制构造函数是public,则说明程序允许对象之间的复制,此时就会通过直接调用自定义构造函数来初始化对象,而不再调用复制构造函数,完成优化”。这位朋友还提到,如果将复制构造函数改为... -
自动调用复制构造函数的情况
2022-01-30 23:37:001)当用一个对象去初始化本类的另一个对象时,会调用复制构造函数。例如,使用下列形式的说明语句时,即会调用复制析构函数。 类名 对象名2(对象名1); 类名 对象名2 = 对象名1; #include <iostream> using ... -
函数返回值是类对象时调用复制构造函数?
2018-12-26 20:18:00调用复制构造函数的三种情况: 1.当用类一个对象去初始化另一个对象时。 2.如果函数形参是类对象。 3.如果函数返回值是类对象,函数执行完成返回调用时。 #include <iostream> using namespace ... -
复制构造函数(拷贝构造函数)在什么情况下被调用
2017-09-15 15:29:25复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性 ,它的形参是本类的对象的引用,比如(类名 对象名)。它的作用是使用一个已经存在的对象(此对象由复制构造函数的参数决定),去初始化同类的一个...