精华内容
下载资源
问答
  • 复制构造函数的参数是什么
    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++类用三个特殊的成员函数:复制构造函数、赋值操作符和析构函数 来决定类对象之间的初始化或赋值时发生什么。所谓的“复制控制”即通过这三个成员函数控制对象复制的过程。本篇文章将介绍复制构造函数。  复制...
  • 2、函数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 语句中的数据初始化。

    展开全文
  • 构造函数 是为了让我们自己去规定 , 我们定义对象的初始化。 #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++ 拷贝构造函数

    2021-01-19 23:44:42
    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象。 复制对象把它作为参数传递给...
  • (其实很多名字,只要静下心来想一想,就真的是顾名思义呀)拷贝又称复制,因此拷贝构造函数又称复制构造函数。百度百科上是这样说的:拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的...
  • 复制构造函数参数传递方式必须按引用来进行传递,请看实例: #include #include using namespace std ; class Student { private : char name[8]; int age ; char sex ; int score ; public : void disp...
  • C++中的复制构造函数

    2021-09-16 14:33:03
    目录普通变量的复制复制构造函数...复制构造函数又叫做拷贝构造函数,它只有一个参数(既然需要复制,一个就够了,若传入两个相同对象则没有意义,若传入两个不同的对象,就没必要叫做复制构造函数了),参数类型为本

    普通变量的复制

    有时我们会在定义一个变量的同时使用另一个变量来初始化它。

    int a_variable=12;
    int new_variable(a_variable);
    

    通过已有的同类型变量来初始化自身很有用。
    对自定义类型的对象是否可以通过一个存在的对象方便的复制呢?

    复制构造函数

    复制构造函数又叫做拷贝构造函数,它只有一个参数(既然需要复制,一个就够了,若传入两个相同对象则没有意义,若传入两个不同的对象,就没必要叫做复制构造函数了),参数类型为本类的引用。
    如果程序员没有编写复制构造函数,编译器会自动生成复制构造函数,在复制构造函数中按照成员变量进行逐字节复制(初学可以这样理解,实际上,个别编译器并不总是会自动生成复制构造函数,它们可能采用直接将源对象的各个值复制到目标对象对应的成员变量上,后面会介绍这种情况)。

    class MyDate
    {
    	int day_;
    	int year_;
    public:
    	MyDate(int day, int year)
    	{
    		day_ = day;
    		year_ = year;
    	}
    	MyDate(const MyDate& date)
    	{
    		day_ = date.day_;
    		year_ = date.year_;
    		cout << "Date类的复制构造函数执行了!" << endl;
    	}
    	~MyDate() {}
    };
    
    void test()
    {
    	MyDate date1(12, 2021);
    	MyDate date2(date1);
    }
    int main()
    {
    	test();
    	system("pause");
    	return 0;
    }
    

    执行为:
    在这里插入图片描述
    对于MyDate(const MyDate& date)参数列表中的const,因为复制构造函数参数另一个对象引用,如果不加const修饰,在此复制构造函数中可能会改变原对象的内容,为了安全起见,应加尽加。

    如果程序员编写了复制构造函数,则编译器就不会生成默认复制构造函数了(所以编写了复制构造函数之后就尽量在函数体内实现复制操作,不要定义了复制构造函数却不完全或不实现复制操作)。

    另一方面,所有的构造函数(包括复制构造函数)、析构函数都无法从父类继承,只能自己实现。

    构造函数如果只有一个参数且这个参数为本类对象,就会与复制构造函数起冲突。如图:
    在这里插入图片描述

    复制构造函数的三种调用

    复制构造函数在以下3种情况下会被调用:
    1.当使用一个A类型对象去初始化另一个A类型的对象时(刚创建好的,已创建好的不算),会调用复制构造函数。注意观察以下代码:

    Date date1(12,2012);//创建一个date1对象
    Date date2(date1);//调用复制构造函数
    Date date3=date1;//也会调用复制构造 函数
    date2=date1;//date2已存在,不会调用复制构造函数,会调用赋值=操作函数
    

    在这里插入图片描述
    可以看到复制构造函数只调用了两次。
    2.我们都知道C++传参有传值和传引用(指针本质上是传值,传的是实参的地址)。如果函数参数是一个自定义对象,那么会调用该自定义对象的复制构造函数。
    在传值的时候,编译器会开辟一个空间(创建了一个临时对象)存储实参的值(这个过程会将实参的各个值分配复制给临时对象),再将该值压入栈中。

    //类声明略
    void TestFunction(MyDate date)
    {
    	cout << "TestFunction()执行了!" << endl;
    }
    void test()
    {
    	MyDate date1(12, 2021);
    	TestFunction(date1);
    }
    //main函数略
    

    结果如下:
    在这里插入图片描述
    3.如果函数的返回值是类MyDate的对象,则函数返回时,会调用该对象的复制构造函数。

    //类声明略
    MyDate TestFunction2()
    {
    	MyDate date1(12, 2021);
    	cout << &date1 << endl;
    	return date1;
    }
    void test()
    {
    	cout << &TestFunction2() << endl;
    }
    //main函数省略
    

    从复制构造函数内的输出被两个地址输出夹住即可看出在哪里调用了复制构造函数。

    在这里插入图片描述

    复制构造函数的禁用

    如果不希望自定义类型的复制构造函数被调用。
    仅仅不编写复制构造函数是不行的,编译器可能会自动生成默认的复制构造函数。应该使用private修饰复制构造函数(这时编译器就不会生成自动复制构造函数),此时不要实现这个复制构造函数,如下:
    在这里插入图片描述
    这样便既禁止了用户调用此复制构造函数,又禁止了用户通过其他成员函数或友元函数间接地调用它(如果我们仅仅把复制构造函数声明为private,声明并实现了复制构造函数,虽然避免了用户直接调用,但成员函数和友元函数还是可以调用,只有不实现它才能永绝后患)。
    Bjarne Stroustrup认为如果你希望禁止某些操作,就把它定义为一个私有的成员函数即可。

    深拷贝与浅拷贝

    如果成员变量含有指针类型,默认复制构造函数并不会将指针指向的内存中的值进行赋值,仅仅将指针存储的值(也就是一个地址)复制了一次(与我们所希望的不一致)。这时两个指针指向了同一块内存空间,一旦一种一个指针所属的对象声明周期结束,会调用它自己的析构函数回收指针指向的内存空间。这时另一个指针遍指向了一个垃圾值,这个指针也变为了空悬指针。以上就是我们常提到的浅拷贝。
    实际开发当中要竭力避免以上清情况的发生(当成员变量含有指针或动态分配内存等情况)。
    深拷贝如下:

    class MyDate
    {
    private:
    	char* buffer_;
    public:
    	MyDate(const char *init);//实现略
    	MyDate(const MyDate &date)
    	{
    		if(date.buffer_!=nullptr)
    		{
    			buffer_=new char[strlen(date.buffer_)+1];
    			strcpy(buffer_,date.buffer_);
    		}
    		else
    		{
    			buffer=nullptr;
    		}
    	}
    }
    

    复制构造函数先检查date中的buffer_的字符串大小,然后分配此大小+1的内存给新创建的对象的buffer_(strlen函数不会计算’\0’字符),最后使用strcpy函数将date的buffer_指向的内存中的内容复制到新创建的对象的buffer_所指向的空间(strcpy函数会吧’\0’字符一并复制)。最后实现了两个指针指向了不同的存储空间(两个空间的内容相同)。
    在这里插入图片描述
    如果我们要编写需要字符的成员时,尽量使用string。它会像其他成员变量一样进行复制,因为string有自己的复制构造函数。

    一定会生成默认复制构造函数吗?

    我们前面提到如果程序员没有定义自己的复制构造函数,编译器会为我们生成一个默认复制构造函数。
    实际上以下4中情况编译器为我们生成默认复制构造函数。
    1.没有为类编写复制构造函数,但该类含有自定义类型或string等类型作为成员变量时。
    2.没有为类编写复制构造函数,但该类继承了一个含有复制构造函数的类时,编译器会生成默认复制构造函数,在该函数中调用父类的复制构造函数。
    3.没有为类编写复制构造函数,但是该类定义了虚函数或者该类的父类定义了虚函数。,
    4.没有为类编写复制构造函数,但是该类有虚基类。

    参考

    The C++ Programming Language (美) Bjarne Stroustrup
    Thinking in C++ Volume One:Introduction toStandard C++ (美)Bruce Eckel
    C++新经典 对象模型 王建伟
    cpp参考:https://zh.cppreference.com

    展开全文
  • c++复制构造函数详解

    千次阅读 多人点赞 2020-07-12 16:32:41
    复制构造函数(Copy constructor)是c++中的一个特殊构造函数,也称拷贝构造函数,它只有一个参数参数类型为同类对象的引用。 如果没有定义复制构造函数,那么编译器将生成默认的复制构造函数。默认的复制构造函数...
  • 拷贝和复制是一个意思,对应的英文单词都是copy。对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的...
  • 如果我们的拷贝构造函数参数不是引用,那么拷贝构造函数又会重复调用拷贝构造函数,就这样永远的递归调用下去了。   所以, 拷贝构造函数是必须要带引用类型的参数的, 而且这也是编译器强制性要求的。 为...
  • 文章目录默认构造函数和复制构造函数1 定义默认构造函数2 隐式类类型转换3 复制构造函数与合成复制构造函数3.1 复制构造函数3.2 合成复制构造函数4 深复制与浅复制 默认构造函数和复制构造函数 1 定义默认构造函数 ...
  • C++——构造函数、析构函数以及复制构造函数

    千次阅读 多人点赞 2021-03-09 16:28:49
    文章目录一、构造函数1.1 构造函数什么?1.2 为什么要有构造函数?1.3 如何使用构造函数?1.4 构造函数的实现二、使用步骤 一、构造函数 在程序执行的过程中,当遇到与对声明语句时,程序会向操作系统申请一定的...
  • 主要介绍了C++中复制构造函数和重载赋值操作符总结,本文对复制构造函数和重载赋值操作符的定义、调用时机、实现要点、细节等做了总结,需要的朋友可以参考下
  • 什么是复制构造函数复制构造函数是一个成员函数,它使用同一个类的另一个对象初始化一个对象。函数原型如下: ClassName(const ClassName &old_obj); 下面是一个复制构造函数的简单示例: #include <...
  • 什么是复制构造函数复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性 ,它的形参是本类的对象的引用,比如(类名 对象名)。它的作用是使用一个已经存在的对象(此对象由复制构造函数参数决定...
  • 然而,在实现拷贝构造函数和赋值符函数时,在函数里利用对象直接访问了私有成员变量,因而,产生了困惑.下面以具体实例进行说明: 疑惑:为什么第26行和第32行代码可以编译通过,而第39行和第40行代码会产生编译...
  • 如果类成员都是简单类型(如标量值),则编译器生成的复制构造函数已足够,无需定义自己的类型。 如果类需要更复杂的初始化,则需要实现自定义复制构造函数。例如,如果类成员是指针,则需要定义复制构造函数以分配...
  • C++类和对象练习 构造函数 复制构造函数 缺省参数列表
  • 详细介绍了构造函数、复制构造函数、拷贝构造函数之间的区别
  • 如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。...
  • 复制构造函数

    千次阅读 多人点赞 2018-09-03 11:11:35
    复制构造函数是一种特殊构造函数,在生成一个实例时,一般会同时生成一个默认的复制构造函数复制构造函数完成一些基于同一类的其他对象的构建及初始化工作。 特点 复制构造函数名与类名同名,因为它是一种...
  • 当新对象被创建的时候,会调用构造函数。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,java... java不支持像C++中那样的复制构造函数,这个不同点是因为如果你不是自己写的构造函数的情况下,jav...
  • 本篇文章是对C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程进行了总结与分析,需要的朋友参考下
  • 如果一个函数是通过值传递(pass by value)的话,那么它真正传递的其实是实参的副本,该副本产生必定会调用复制构造函数。那么,试想一下,如果我们的复制构造函数是通过值传递的话,它就会调用它本身来产生一个副本...
  • C++ 赋值构造函数 复制构造函数

    千次阅读 2018-08-25 15:29:11
    编译器提供一个不接受任何参数,也不执行任何操作的构造函数,称之为默认构造函数 这是因为创造对象的时候总会调用默认构造函数 Klunk::Klunk() {} //定义 Klunk lunk; //声明 使用默认构造函数 如果定义了构造...
  • 拷贝构造函数(复制构造函数

    千次阅读 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...
  • 下面小编就为大家带来一篇详谈C++何时需要定义赋值/复制构造函数。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • C++中类的构造函数与复制构造函数

    千次阅读 2017-04-27 10:52:42
    在网络上有朋友提到“主要原因在于编译器的优化,当复制构造函数是public时,编译器就会根据这个特性来对代码进行优化。当程序运行时,编译器发现复制构造函数是public,则说明程序允许对象之间的复制,此时就会通过...
  • 拷贝构造函数(复制构造函数) 一、拷贝构造函数概念 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 262,185
精华内容 104,874
关键字:

复制构造函数的参数是什么