首页 > 其他分享 >构造函数的调用规则、深拷贝与浅拷贝

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

时间:2024-11-10 15:18:52浏览次数:3  
标签:调用 堆区 默认 num 拷贝 构造函数 size

目录

1.调用规则

2.深拷贝和浅拷贝问题

3.string类的拷贝构造练习

1.调用规则

默认情况下,C++至少会给一个类添加三个函数:
        1.默认构造函数(无参,函数体为空)

        2.默认析构函数(无参,函数体为空)

        3.默认拷贝构造函数,对属性进行值拷贝

调用规则:

        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;
}

标签:调用,堆区,默认,num,拷贝,构造函数,size
From: https://blog.csdn.net/gemluoye/article/details/143641847

相关文章

  • wordpress站外调用指定ID分类下的推荐内容
    在WordPress中,如果你想从站外调用指定ID分类下的推荐内容,你可以使用WordPressRESTAPI来实现。以下是一个基本的步骤指南:1.启用RESTAPI确保你的WordPress站点已经启用了RESTAPI。大多数现代WordPress版本默认启用此功能。2.获取分类ID首先,你需要知道你要调用的分类的I......
  • PbootCMS基本调用标签大全
    首页、栏目页、内页的标题、关键词、描述:首页:<title>{pboot:sitetitle}</title>栏目页:<title>{pboot:if('{sort:title}'==''){pboot:pagetitle}{else}{sort:title}{/pboot:if}</title>内页:<title>{content:title}-{pboot:sitetitle}</titl......
  • 在Windows中,使用批处理(.bat)文件可以通过调用命令来实现自动拨号连接。以下是通过批处
    在Windows中,使用批处理(.bat)文件可以通过调用命令来实现自动拨号连接。以下是通过批处理文件实现PPPoE(拨号)连接的基本步骤:步骤1:配置拨号连接(PPPoE)在Windows中,你首先需要设置一个PPPoE拨号连接。以下是设置步骤:打开网络连接设置:点击开始菜单,输入“网络和共享中心”并打开......
  • Playwright使用Typescript实现在测试case文件中调用另一个文件中的方法
    前提:(1)安装了nodejs(2)创建了测试目录(3)使用Vscode安装了Playwright插件可以参考官方文档:https://playwright.dev/docs/getting-started-vscode 在vscode界面最左侧的按钮选择Explorer,创建一个与tests目录同级的目录methods,并在methods目录下创建文件method1.ts,目录结构如......
  • vue通过ollama接口调用开源模型
    先展示下最终效果: 第一步:先安装ollama,并配置对应的开源大模型。安装步骤可以查看上一篇博客:ollama搭建本地ai大模型并应用调用 第二步:需要注意两个配置,页面才可以调用1)OLLAMA_HOST="0.0.0.0:11434"2)若应用部署服务器后想调用,需要配置:OLLAMA_ORIGINS=* 第三步:js流式调......
  • Linux fsync和fdatasync系统调用实现分析(Ext4文件系统)
    Linux系统中,对文件系统上文件的读写一般是通过页缓存(pagecache)进行的(DirectIO除外),这样设计的可以延时磁盘IO的操作,从而可以减少磁盘读写的次数,提升IO性能。但是性能和可靠性在一定程度上往往是矛盾的,虽然内核中设计有一个工作队列执行赃页回写同磁盘文件进行同步,但是在一些极端的......
  • c++--拷贝构造函数&友元函数
    目录1.拷贝构造函数是什么2.拷贝构造函数的基本格式2.1默认拷贝构造函数(浅拷贝)2.2深拷贝(DeepCopy)2.3浅拷贝(ShallowCopy)2.3浅拷贝和深拷贝总结2.友元函数1.拷贝构造函数是什么拷贝构造函数是一个特殊的构造函数,用于在创建新对象时,用已有对象的数据来初始......
  • Oracle 存储过程分页 + Sqlsugar调用
    一、Oracle存储过程分页1createPROCEDUREGetPatientVisitData(2p_HospIdINVARCHAR2,--院区编码3p_strDateINVARCHAR2,--开始日期4p_endDateINVARCHAR2,--结束日期5p_page_sizeINNUMBER,--每页记录数6p_page_numberIN......
  • KeilC51不编译未调用的函数
    KeilC51不编译未调用的函数在用KeilC51做开发时,难免会增删功能,出现定义了的函数未调用的情况。编译时会发出“UNCALLEDSEGMENT,IGNOREDFOROVERLAYPROCESS”的警告,还会浪费程序存储空间。有一个办法,可以不用手动去删除,也不会将这部分代码编译链接进最终的程序。方法如下:......
  • Sqlsugar调用Oracle的存储过程
    前段时间在搬迁项目的时候,遇到一个问题,就是用sqlsugar调用oracle的存储过程的时候调用不了;当时卡了一整天,现在有空了把这个问题记录分享一下。先去nuget上安装一下sqlsugar的包:再安装一个oracle的驱动:添加一下Json包:再去创建一下连接 再创建一个测试用的存储过程crea......