拷贝构造函数与赋值构造函数

#include <QCoreApplication>
#include "stdio.h"
#include "string.h"

class PERSON
{
public:
    int age;
    char* name;
    PERSON()
    {
        printf("construct with 0 para\n");
        age = 1;
        name = new char[7];//7=strlen(""NoName)+1;
        strcpy(this->name, "NoName");
    }
    PERSON(int age_tmp, const char* name_tmp)
    {
        printf("construct with 2 para\n");
        age = age_tmp;
        int name_tmp_len = strlen(name_tmp)+1;//strlen不计\0
		name = new char[name_tmp_len]; 
		strcpy(this->name, name_tmp);//应当加入name_tmp的字符串合法性检查,这里略了
	 } 
	~PERSON()
    { 
        printf("dis_construct\n"); 
        delete [] name;//删除数组内存堆 
    } 
    PERSON(PERSON &other) 
    { 
        printf("copy construct\n"); 
        this->age = other.age;	
        int other_len = strlen(other)+1;//strlen不计\0 
        this->name = new char[other_len];
        strcpy(this->name, other.name);	
    } 
    PERSON& operator =(const PERSON & other)//赋值运算符 
    { 
        printf("operator =\n"); 
        this->age = other.age; 
        strcpy(this->name, other.name); 
        return *this; 
    }
};
void person_age_add(PERSON pers)
{ 
    pers.age++;
}
PERSON get_person_age(void)
{ 
    printf("creat local var\n"); 
    PERSON a(10,"lucy"); 
    printf("creat local person completed\n"); 
    return a;
}
int main()
{ 
    printf("start\n"); 
    printf("part a:\n"); 
    PERSON a(20,"tom"); 
    printf("\npart b:\n"); 
    PERSON b=a; 
    printf("\npart c:\n"); 
    PERSON c(a);
    printf("\npart d-1:\n");
    PERSON d;
    printf("part d-2:\n");
    d = a;
    printf("\npart transfer parameter:\n");
    person_age_add(a);
    printf("\npart transfer ret_val:\n");
    a = get_person_age();
    printf("\n end\n");
    return 0;
}



以上程序在QT5.6中可以编译运行,

结果如下:


**************************************************************************************************************

问题:什么时候拷贝构造函数会被调用?

(1)一个对象以值传递的方式传入函数体

(2)一个对象以值传递的方式从函数返回

(3)一个对象需要通过另一个对象进行初始化

当然,情况(1)(2)本质上都属于情况(3),都是情况(3)的具体应用。以把实参传入形参的过程为例,从汇编侧面上看,形参位于内存栈中,在调用这类函数时,第一步就是:把实参“深拷贝”到内存栈中,所谓深拷贝,就是把对象中的指针成员指向的内容一块拷贝到栈中(同时该形参的指针成员的指向也被修改为指向栈中的内容),这样才能保证函数内收到的形参和实参是一模一样的(除了指针成员本身的值以外);如果实参到形参的传递过程使用了浅拷贝,后果是:形参的指针成员的值和实参的指针成员的值是一模一样的,也即,形参的指针成员竟然指向了实参指针成员指向的位置,那么函数体内如果对形参指针成员指向的内容进行了修改操作,那么等函数返回以后,实参指针成员指向的内容也就变了!这种恶劣的情形同时还伴随着另外两种恶劣情形:①被赋值的指针成员的值被修改掉了,那么它原先指向的内存将无法被释放,内存泄漏,同时这也导致了情况②,②赋值和被赋值的两个类指针成员的值指向了同一块内存,两个类析构时会释放同一块内存,同一块内存释放两次将导致不可预知的结果。

传递返回值和传递实参,情形是一样的。

由上述分析过程可见,对于带有指针成员的对象,作为实参传入函数时必须使用深拷贝,然而,实参指针成员指向的区域有多大,拷贝多少内容入栈,这些东西编译器是不知道的,这时必须靠程序员编写的深拷贝函数来明确,这个深拷贝函数就是拷贝构造函数,例如上面PERSON(PERSON &other)。

结论:

1、值传递时:实参传入形参,或者传回返回值的过程,这两种传递过程就是拷贝构造函数的执行过程,如果程序员没有人为地定义拷贝构造函数,编译器会默认生成一个浅拷贝的拷贝构造函数。因此,对于有指针成员的类,程序员必须手工创建深拷贝的拷贝构造函数,否则void func(PERSON per);  fun(a);,对形参per的指针成员指向内容的操作,会影响实参a。

2、编译器默认生成的拷贝构造函数,功能与默认生成的重载等号运算符完全一样,都是浅拷贝。因此,对于有指针成员的类,程序员必须手工重载深拷贝的等号运算符,否则使用了a=b之后,对a的指针成员指向的内容的操作会影响b。

3、手工创建的重载等号运算符函数、拷贝构造函数,除了要手动拷贝指针成员指向的内容以外,普通的成员变量也得一一传值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值