C++——拷贝构造函数

void fun2(Stack s)
{

}
int main()
{
    Stack s1;
    fun2(s1);
    return 0;
}

这里,fun2函数中的形参s对s1这个实参进行拷贝。但是会发生一些错误。

C++——拷贝构造函数_第1张图片

这里的问题其实是浅拷贝问题。

C++——拷贝构造函数_第2张图片

 其中,栈中会有main函数的栈帧和fun2函数的栈帧。s1中的其他成员变量拷贝给s都没问题,但是其中的a是在堆区malloc出来的。此时,s1会把a这块空间的地址也赋值一份给s,让s的a也指向这块空间。

但是,我们知道,析构函数是后创建的先调用。析构函数的调用顺序为——s——s1。

也就是把a也给释放了,所以s1中的a就变成了野指针。

这时,s1再次调用析构函数对a这块空间再次释放时,就会发生代码崩溃的情况。

【C语言默认情况下不会发生,因为C语言中不会自动调用析构函数。】

对于浅拷贝问题的解决方案:

引用

void fun2(Stack &s)//引用
{

}
int main()
{
    Stack s1;
    fun2(s1);
    return 0;
}

但是引用使用后,s的改变就会影响s1(push,pop...),如果想要s的改变对s1没有影响的话,就得真正的进行拷贝。所以,我们用到了拷贝构造函数。

【BTW】:引用对象实质上是变量的一个别名,不会调用析构函数。

拷贝构造函数

拷贝构造函数也是一种特殊的成员函数,特征如下:

1.拷贝构造函数是构造函数的一个重载形式。

2.拷贝构造函数的参数只有一个且必须是类型对象的引用,使用传值方编译器直接报错,因为会产生无限递归,如下:

Stack(Stack s)    //如果拷贝构造是直接传值               
    {
        s.capacity = capacity;
        s.top = top;
        s.a = a;
    }

int main()

{

        Stack s;     

        Stack s1(s);

//这里调用拷贝构造函数,当传值给形参Stack s时就得调用拷贝构造函数,所以是每当进行一个传值,就调用一次拷贝构造函数,进行无限递归。

        return 0;

}

其实就是因为自定义类型传参的时候,要调用拷贝构造函数。

比如,如下调用函数

C++——拷贝构造函数_第3张图片

这样传值是不行的。

C++——拷贝构造函数_第4张图片

所以最终的拷贝构造函数的形式为:引用

Stack(const Stack& s)      //加上const养成习惯,防止下面赋值赋反了。             
    {
        s.capacity = capacity;
        s.top = top;
        s.a = a;
    }

如上是我们自己写的有关于自定义类型的拷贝构造函数,但是如果我们没有写某些类的拷贝构造函数,系统也会自动生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

【注意】:默认的拷贝构造函数对于内置类型,会进行值拷贝(浅拷贝)。而对于自定义类型,会调用它的拷贝。

总结:Date类型不需要我们写拷贝构造函数,默认生成的就够用了。而Stack类型需要我们写拷贝构造函数来完成深拷贝,否则会在析构阶段产生错误。

你可能感兴趣的:(c++)