当前位置: 代码网 > it编程>编程语言>Java > 解读构造函数的调用规则、深拷贝与浅拷贝

解读构造函数的调用规则、深拷贝与浅拷贝

2024年11月14日 Java 我要评论
1.调用规则默认情况下,c++至少会给一个类添加三个函数:默认构造函数(无参,函数体为空)默认析构函数(无参,函数体为空)默认拷贝构造函数,对属性进行值拷贝调用规则:1.如果用户定义了有参构造函数,c

1.调用规则

默认情况下,c++至少会给一个类添加三个函数:

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝

调用规则:

1.如果用户定义了有参构造函数,c++将不在提供默认无参构造函数,但是会提供默认拷贝构造函数。

如上图,我们给类a提供了一个有参的构造函数,所以此时,如果再去主函数定义一个无参的对象时,编译器就会提示“类a不存在默认构造函数”,这就说明如果自己定义了有参构造函数,那么就不会提供默认的无参构造函数。

#include<iostream>
using namespace std;
class a {
public:
	int num;
	a(int num) {
		this->num = num;
	}
};
int main()
{
	a a(5);
	a b(a);
	cout << b.num;
	return 0;
}

在类中我们只提供了一个有参的构造函数,我们在主函数中定义一个b对象,传的是对象a,那么它便会走拷贝构造的函数,如果能够输出b.num的值为5的话,则说明提供了默认的拷贝构造函数。

通过打印b的num,发现确实是5.那么就会提供默认的构造函数。

2.如果用户定义拷贝构造函数,c++不会提供其他构造函数

可以看到当我们仅在类a中写了一个拷贝构造函数时,再去主函数中定义一个无参的对象a时,就会报错,提示“类a不存在默认构造函数”。

2.深拷贝和浅拷贝问题

浅拷贝:就是简单的赋值操作

存在的问题:如果有指针指向堆区内存时,不同对象的指针成员指向的是同一块堆区内存。在对象进行释放时,该堆区会被释放两次。当一个对象修改堆区的内容时,另一个对象的内容也会随着改变。

深拷贝:申请同样大小的堆区内存,保证两个堆区的内容一样。

#include<iostream>
using namespace std;
class a {
    int num;
    int* p;
public:
    a() {
        num = 0;
        p = nullptr;
        cout << "调用无参构造" << endl;
    }
    a(int a) {
        num = a;
        p = new int[num];
        cout << "调用有参构造" << endl;
    }
    ~a() {
        if (p)delete[]p;
    }
};

int main() {
    a a(3);
    a b = a;
    return 0;
}

在类a中我们定义了一个指针变量,在有参构造函数中,我们在堆区开辟了一块空间。在主函数数中,定义对象b要走拷贝构造函数。下面我们来运行这段代码:

发现报错了,原因就是对象a和对象b的成员变量*p指向的是同一块堆区内存,再调用析构函数时,对象b先把这块堆区内存释放了,当对象a调用析构函数时,此时那块堆区内存已经不存在了,所以会出现访问内存失败,导致程序崩溃。

那么解决的办法就是重写拷贝构造函数,让他们指向不同的堆区区域。代码如下:

#include<iostream>
using namespace std;
class a {
    int num;
    int* p;
public:
    a() {
        num = 0;
        p = nullptr;
        cout << "调用无参构造" << endl;
    }
    a(int a) {
        num = a;
        p = new int[num];
        cout << "调用有参构造" << endl;
    }
    a(const a& other) {//万能引用,避免实参修改形参
        num = other.num;
        p = new int[num];//保证内存大小相同
        for (int i = 0; i < num; i++) {
            p[i] = other.p[i];//保证数据相同
        }
        cout << "调用拷贝构造" << endl;
    }
    ~a() {
        if (p)delete[]p;
        cout << "调用析构函数" << endl;
    }
};

int main() {
    a a(3);
    a b = a;
    return 0;
}

通过再在堆区上开辟一块内存,让这块堆区内存上的内容和对象a的堆区内容一样就行,这就是深拷贝。

程序能正常运行。

3.string类的拷贝构造练习

可以通过下面这个自定义string类进一步加深对深拷贝的理解。

#include<iostream>
using namespace std;
class string {
    int size;
    char* p;
public:
    string() {
        p = nullptr;
        size = 0;
    }
    string(const char* p) {
        size = strlen(p);
        this->p = new char[size + 1];//\0,所以加一
        strcpy_s(this->p, size + 1, p);
    }
    ~string() {
        if (p)  delete[]p;
    }
    string(const string& other) {
        this->size = other.size;
        this->p = new char[size + 1];
        strcpy_s(this->p, size + 1,other.p);
    }
    void print() {
        cout << p<<endl;
    }
};
int main() {
    string str = "abcde";
    string str2(str);
    str2.print();
    return 0;
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com