C/C++拷贝构造函数举例

C/C++拷贝构造函数举例


目录
1.拷贝构造函数定义
2.默认拷贝构造函数
3.显式拷贝构造函数
4.参数传递时,调用拷贝构造函数
5.对象作为返回值,调用拷贝构造函数
6.拷贝构造函数,危害(浅拷贝)
7.拷贝构造函数,常用(深拷贝)
8.拷贝构造函数,私有(深拷贝)


正文

1. 拷贝构造函数定义
拷贝构造函数是一种对构造函数的重载。与构造函数有着相同的作用,在创建新对象时,将数值赋值给它们的成员函数。

2.默认拷贝构造函数
若没有将拷贝构造函数显式地定义出来,类具有默认的拷贝构造函数。下记程序里只有带参数arg的构造函数,并未定义拷贝构造函数。

//例1
#include <iostream>
#include <stdio.h>

using namespace std;

class CExample {
    private:
        int a;
    public:
        CExample(int arg) {
            printf("func: %s\n", __FUNCTION__);
            a = arg;
        }
        void show() {
            printf("func:%s, a:%d", __FUNCTION__, a);
        }
};

int main()
{
    CExample A(100);
    CExample B = A; // 或者写为 CExample B(A); 结果一样,都是调用默认拷贝构造函数。 
    B.show();
    return 0;
}

输出:
func: CExample
func:show, a:100

3.显式拷贝构造函数
如果类中显式定义拷贝构造函数,则系统会执行定义的显式构造函数。

//例2
#include <iostream>
#include <stdio.h>

using namespace std;

class CExample {
    private:
        int a;
    public:
        CExample(int arg) {
            printf("func: %s(int arg)\n", __FUNCTION__);
            a = arg;
        }
        CExample(const CExample &C) {
            printf("func: %s(const)\n", __FUNCTION__);
            a = C.a;
        }
        void show() {
            printf("func:%s, a:%d", __FUNCTION__, a);
        }
};

int main()
{
    CExample A(100);
    CExample B = A; // 或者写为, CExample B(A); 
    B.show();
    return 0;
}

输出:
func: CExample(int arg)
func: CExample(const)
func:show, a:100

4.参数传递时,调用拷贝构造函数
对象作为函数参数传递给形参时,调用形参的拷贝构造函数。
将对象test作为实参,传递给g_func()函数时,形参CExample C被赋值为test,相当于执行了CExample C=test;
如果类CExample无显式拷贝构造函数,则执行默认构造函数;
下记类CExample有显示拷贝构造函数CExample(const CExample &C){…},执行该函数。

//例3
#include <iostream>
#include <stdio.h>

using namespace std;

class CExample {
    private:
        int a;
    public:
        CExample(int arg) {
            printf("func: %s(int arg)\n", __FUNCTION__);
            a = arg;
        }
        CExample(const CExample &C) {
            printf("func: %s(const)\n", __FUNCTION__);
            a = C.a;
        }
        void show() {
            printf("func:%s, a:%d\n", __FUNCTION__, a);
        }
        ~CExample() {
            printf("func:%s\n", __FUNCTION__);
        }
};

void g_func(CExample C) 
{
    printf("func:%s\n", __FUNCTION__);
    C.show();
}

int main()
{
    CExample test(1);
    printf("func:%s, call g_func start\n", __FUNCTION__);
    g_func(test);
    printf("func:%s, call g_func end\n", __FUNCTION__);
    return 0;
}


输出:
func: CExample(int arg)
func:main, call g_func start
func: CExample(const)
func:g_func
func:show, a:1
func:~CExample
func:main, call g_func end
func:~CExample

5.对象作为返回值,调用拷贝构造函数
存疑:1.为什么拷贝构造函数没有被调用?2.为什么析构函数只打印了1次?

//例4
#include <iostream>
#include <stdio.h>

using namespace std;

class CExample {
    private:
        int a;
    public:
        CExample(int arg) {
            printf("[%s(arg)]\n", __FUNCTION__);
            a = arg;
        }
        CExample(const CExample &C) {
            printf("[%s(const)]\n", __FUNCTION__);
            a = C.a;
        }
        void show() {
            printf("[%s]a:%d\n", __FUNCTION__, a);
        }
        ~CExample() {
            printf("[%s]\n", __FUNCTION__);
        }
};

CExample g_func() 
{
    printf("[%s]\n", __FUNCTION__);
    CExample temp(1);
    return temp;
}

int main()
{
    printf("[%s]call g_func start\n", __FUNCTION__);
    CExample A = g_func();
    A.show();
    printf("[%s]call g_func end\n", __FUNCTION__);
    return 0;
}

输出:
[main]call g_func start
[g_func]
[CExample(arg)]
[show]a:1
[main]call g_func end
[~CExample]


6.拷贝构造函数,危害(浅拷贝)
默认拷贝构造函数属于浅拷贝,是指仅简单地拷贝值,一旦成员变量中含有动态成员,则析构或者使用时容易产生野指针,或地址访问错误等问题。这种指针错乱无法通过等于null或者其他的判断检查,非常危险。

//例5
#include <iostream>
#include <stdio.h>

using namespace std;

class Rect {
    public:
        Rect() {
            p = new int(100);
            printf("[%s()]p->%d\n", __FUNCTION__, &(*p));
        }
        ~Rect() {
            if (p != NULL) {
                delete p;
                printf("[%s]p->%d\n", __FUNCTION__, &(*p));
            }
            else {
                printf("[%s]p->null\n", __FUNCTION__);
            }
        }
        void show() {
            printf("[%s]p->%d\n", __FUNCTION__, &(*p));
        }
    private:
        int width;
        int height;
        int *p;
};

int main()
{
    Rect rect1;
    rect1.show();
    Rect rect2(rect1);
    rect2.show();
    return 0;
}


输出:
[Rect()]p->536937200
[show]p->536937200
[show]p->536937200
[~Rect]p->:536937200
Aborted (core dumped)

疑问:p指向了同地址,执行1次析构了以后,就会报错,并且没有输出p->null的log?
解答:两次执行析构函数,会先析构rect2,而后析构rect1。
执行rect2的析构函数delete p之后,rect2.p不会自动将指向null。
即使在delete p;之后再加上p=null,也只是将rect2.p=null。
在之后执行rect1的析构函数时,rect1.p不等于null,rect1.p仍然等于536937200
即,如下记代码所示.

//例6
#include <iostream>
#include <stdio.h>

using namespace std;

class Rect {
    public:
        Rect() {
            p = new int(100);
            printf("[%s()]*p:%d,p:%d\n", __FUNCTION__, *p, p);
        }
        ~Rect() {
            if (p != NULL) {
                printf("[%s]*p:%d, p:%d\n", __FUNCTION__, *p, p);
                delete p;
                p = NULL;
            }
            else {
                printf("[%s]p==null\n", __FUNCTION__);
            }
        }
        void show() {
            printf("[%s]p:%d\n", __FUNCTION__, p);
        }
    private:
        int width;
        int height;
        int *p;
};

int main()
{
    Rect rect1;
    Rect rect2(rect1);
    rect2.~Rect();
    return 0;
}
输出:
[Rect()]*p:100,p:536937200
[~Rect]*p:100, p:536937200
[~Rect]p==null
[~Rect]*p:1630022424, p:536937200
Aborted (core dumped)

7.拷贝构造函数,常用(深拷贝)
当类含有动态成员变量时,要使用显式拷贝构造函数对动态成员变量赋值。在拷贝构造函数中对动态成员变量重新创建新空间。

//例7
#include <iostream>
#include <stdio.h>

using namespace std;

class Rect {
    public:
        Rect() {
            p = new int(100);
            printf("[%s()]p:%d,*p:%d\n", __FUNCTION__, p, *p);
        }
        Rect(const Rect &r) {
            p = new int;
            *p = *(r.p);
            printf("[%s(const)]p:%d, *p:%d\n", __FUNCTION__, p, *p);
        }
        ~Rect() {
            if (p != NULL) {
                delete p;
                printf("[%s]p:%d, *p\n", __FUNCTION__, p, *p);
            }
            else {
                printf("[%s]p==null\n", __FUNCTION__);
            }
        }
        void show() {
            printf("[%s]p:%d\n", __FUNCTION__, p);
        }
    private:
        int width;
        int height;
        int *p;
};

int main()
{
    Rect rect1;
    Rect rect2(rect1);
    return 0;
}


输出:
[Rect()]p:536937192,*p:100
[Rect(const)]p:537166672, *p:100
[~Rect]p:537166672, *p
[~Rect]p:536937192, *p

8.拷贝构造函数,私有(深拷贝)
当类含有动态成员变量时,想要禁用拷贝构造函数,可将其设置为私有成员函数。

//例8
#include <iostream>
#include <stdio.h>

using namespace std;

class Rect {
    public:
        Rect() {
            p = new int(100);
            printf("[%s()]*p:%d,p:%d\n", __FUNCTION__, *p, p);
        }
        ~Rect() {
            if (p != NULL) {
                printf("[%s]*p:%d, p:%d\n", __FUNCTION__, *p, p);
                delete p;
                p = NULL;
            }
        }
        void show() {
            printf("[%s]p:%d\n", __FUNCTION__, p);
        }
    private:
        Rect(const Rect &r); 
        int width;
        int height;
        int *p;
};

int main()
{
    Rect rect1;
    Rect rect2(rect1);
    return 0;
}
输出:

test.cpp: 在函数‘int main()’中:
test.cpp:23:9: 错误:‘Rect::Rect(const Rect&)’是私有的
         Rect(const Rect &r);
         ^
test.cpp:32:21: 错误:在此上下文中
     Rect rect2(rect1);

参考文章:
[1]https://blog.csdn.net/lwbeyond/article/details/6202256
[2]C++Primer Plus(第六版)中文版

猜你喜欢

转载自blog.csdn.net/NeptuneYs/article/details/107169182