从0开始C++(三):构造函数与析构函数详解

news2024/11/26 3:52:01

目录

构造函数 

构造函数的基本使用

构造函数也支持函数重载

构造函数也支持函数参数默认值

构造初始化列表

拷贝构造函数

浅拷贝和深拷贝

析构函数

 总结

练习一下ヽ( ̄▽ ̄)ノ 


构造函数 

构造函数的基本使用

构造函数是一种特殊的成员函数,用于创建对象时初始化,写法上有以下要求:

● 函数名称必须与类名完全一样。

● 构造函数不写返回值

● 如果程序员不手动编写构造函数,编译器就会自动添加一个默认无参数的构造函数。

● 手动添加构造函数后,编译器就不会自动添加默认无参构造函数。

class Car
{
private://权限:最私有的权限
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:最开放的权限
    Car(string b,string m,int w) //手动添加的构造函数
    {
        brand=b;
        modle=m;
        weight=w;
    }

    string get_brand()   //外部函数接口
    {
        return brand;
    }
    string get_modle()
    {
        return modle;
    }
    int get_weight()
    {
        return weight;
    } 
};
int main()
{
    Car *myCar = new Car; //  创建堆内存对象
    //cout << Car.brand << endl;  //错误,brand是私有成员 不能外部访问
    cont << Car.get_brand << endl; //可以使用预留的接口访问 
}

构造函数也支持函数重载

class Car
{
private://权限:最私有的权限
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:最开放的权限
    Car(string b,string m,int w) // 有参构造函数
    {
        brand=b;
        modle=m;
        weight=w;
    }
    Car()    // 无参构造函数
    {
        brand="xiaomi";
        modle="su7";
        weight=1500;
    }
    string get_brand()   // 外部函数接口
    {
        return brand;
    }
    string get_modle()
    {
        return modle;
    }
    int get_weight()
    {
        return weight;
    } 
};
int main()
{
    Car *myCar = new Car; //  调用无参构造函数
    Car *myCar2 = new Car("HuaWei","问界","2000"); // 此时调用有参构造函数
    
}

构造函数也支持函数参数默认值

从第一个设置默认值的变量开始,在其变量之后的所有参数都要加默认值,在他之前的变量可以不加默认值。

class Car
{
private://权限:最私有的权限
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:最开放的权限
    Car(string b="xiaomi",string m="su7",int w=1500) // 全缺省时,不能和无参构造函数同时存在
    {
        brand=b;
        modle=m;
        weight=w;
    }
    //    Car()    // 无参构造函数
    //    {
    //        brand="xiaomi";
    //        modle="su7";
    //       weight=1500;
    //    }
    string get_brand()   // 外部函数接口
    {
        return brand;
    }
    string get_modle()
    {
        return modle;
    }
    int get_weight()
    {
        return weight;
    } 
};
int main()
{
    Car *myCar = new Car; //  全缺省
    Car *myCar2 = new Car("问界","M7","2000"); // 此时调用有参构造函数
    
}

构造初始化列表

当构造函数的局部变量与成员变量重名时可以使用构造初始列表的方式区分,此外,使用构造初始化列表还可以给被 const 修饰的成员变量赋值。

class Car
{
private://权限:最私有的权限
    const string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:最开放的权限
    Car(string b,string modle,int w):brand(b),modle(modle),weight(w){} // 用构造初始化列表的方式赋值
    string get_brand()   // 外部函数接口
    {
        return brand;
    }
    string get_modle()
    {
        return modle;
    }
    int get_weight()
    {
        return weight;
    } 
};
int main()
{
    Car *myCar = new Car("问界","M7","2000"); // 此时调用有参构造函数
    
}

拷贝构造函数

C++的拷贝构造函数是一种特殊的成员函数,用于创建一个对象的副本。它的参数是一个对象的引用,通过这个参数可以将一个对象的值复制给另一个对象。

拷贝构造函数通常在以下情况下被调用:

1、当用一个对象初始化另一个对象时,会调用拷贝构造函数。例如:

class MyClass {
public:
    MyClass(const MyClass& obj) {
        // 拷贝构造函数的实现
    }
};

MyClass obj1;
MyClass obj2 = obj1;  // 调用拷贝构造函数

 2、当将一个对象作为函数参数传递给函数时,会调用拷贝构造函数。例如:

void func(MyClass obj) {
    // 函数体
}

MyClass obj;
func(obj);  // 调用拷贝构造函数

3、当函数返回一个对象时,会调用拷贝构造函数。例如:

MyClass func() {
    MyClass obj;
    // 对 obj 进行初始化和操作
    return obj;  // 调用拷贝构造函数
}

需要注意的是,默认情况下,C++会生成一个默认的拷贝构造函数,该函数会将对象的所有成员变量进行一一拷贝。但如果类中存在指针或其他资源,需要手动编写拷贝构造函数来处理这些资源的拷贝问题,以防止浅拷贝带来的问题。

浅拷贝和深拷贝

C++中的拷贝操作有浅拷贝和深拷贝两种方式。

浅拷贝是指拷贝对象时,只是简单地将一个对象的数据成员的值复制给另一个对象的对应数据成员,而不会复制指向动态分配内存的指针。这意味着两个指针将指向同一块内存,当其中一个对象释放这块内存时,另一个对象的指针将成为悬空指针。例如:

class MyClass {
public:
    int* data;

    MyClass(const MyClass& other) : data(other.data) {
        // 拷贝构造函数的实现
    }
};

MyClass obj1;
obj1.data = new int(5);

MyClass obj2 = obj1;  // 浅拷贝

delete obj1.data;  // 释放内存
cout << *obj2.data;  // 可能会输出无效的值

深拷贝是指拷贝对象时,除了复制数据成员的值外,还会为每个指针成员分配一块新的内存,并将源对象的值复制到新的内存中,以确保两个对象之间的指针成员指向不同的内存块。这样即使一个对象释放了内存,另一个对象的指针仍然有效。例如:

class MyClass {
public:
    int* data;

    MyClass(const MyClass& other) : data(new int(*other.data)) {
        // 拷贝构造函数的实现
    }

    ~MyClass() {
        delete data;
    }
};

MyClass obj1;
obj1.data = new int(5);

MyClass obj2 = obj1;  // 深拷贝

delete obj1.data;  // 释放内存
cout << *obj2.data;  // 仍然可以正常输出

析构函数

C++中的析构函数是一种特殊的成员函数,用于在对象的生命周期结束时执行清理操作。析构函数的名称与类的名称相同,前面加上一个波浪号(~)作为前缀,没有返回类型,也没有参数。

析构函数在以下情况下被调用:

  1. 当对象的作用域结束时,例如,当对象在函数中定义并在函数结束时销毁。
  2. 当对象是另一个对象的成员,并且该对象的析构函数被调用时。
  3. 当使用delete关键字显式释放通过new关键字配分的内存时。

析构函数的主要目的是释放对象分配的资源,例如动态分配的内存、打开的文件等。它可以通过在析构函数中使用delete关键字来释放内存,或者通过关闭文件句柄等操作来释放资源。

以下是一个示例,展示了一个类的析构函数的基本用法:

class MyClass {
public:
    MyClass() {
        cout << "构造函数被调用" << endl;
    }

    ~MyClass() {
        cout << "析构函数被调用" << endl;
    }
};

int main() {
    MyClass obj;  // 创建一个对象

    // 在此处执行其他操作

    return 0;  // 对象的作用域结束,析构函数被调用
}

当对象的作用域结束时,析构函数将被自动调用,输出如下结果:

构造函数被调用
析构函数被调用

需要注意的是,如果类中使用了动态分配的内存或其他资源,在析构函数中应该对这些资源进行释放,以避免内存泄漏或资源泄漏的问题。

 总结

构造函数

析构函数

创建对象时手动调用

当对象销毁时,自动调用

函数名称是类名

函数名称是~类名

构造函数可以重载

析构函数没有参数,不能重载

用于创建对象时并初始化

用于销毁对象时释放资源

有返回值但是不写,返回值是新创建的对象

没有返回值

练习一下ヽ( ̄▽ ̄)ノ 

写一个Dog类,要求有性别、年龄和品种三个属性,属性值封装,使用构造函数传参初始化。增加函数Dog* birth(const Dog& d),在函数体内部判断d与当前狗对象的属性值,当满足以下条件时,返回新创建的狗对象:

● 两条狗的年龄2-5

● 一公一母

新创建的狗对象的属性满足以下条件:

● 年龄:1岁

● 性别:随意

● 品种:

○ 如果父母的品种一样,品种就是父母的品种

○ 如果父母的品种不一样,品种是父母品种的拼合(自己制定拼合逻辑,或者直接是父母的品种之一)

如果两条狗不能生育,返回NULL。

参考代码

#include <iostream>
#include<string.h>
#include<time.h>
using namespace std;

class Dog
{
private:
    int sex;  //0母 1公
    int age;  //小于2或大于5不能生育
    char *variety=new char[20];

public:
    Dog(int s,int a,char *v)
    {
        if(s==3) //  性别随机
        {
            sex=time(NULL)%2;
        }
        else
        {
            sex=s;
        }
        age=a;
        strcpy(variety,v);
    }
    Dog* brith(const Dog &D)
    {
        if(sex != D.sex)
        {
            if( age < 2 || age > 5 || D.age < 2 || D.age > 5 )
            {
                return NULL;
            }
            Dog* NewDog = new Dog(3,1,D.variety);
            return NewDog;
        }
        return NULL;
    }

    int get_sex()
    {
        return sex;
    }
    int get_age()
    {
        return age;
    }
    char *get_variety()
    {
        return variety;
    }


};

int main()
{
    int sex,age;
    char variety[20];
    while(1)
    {
        cout << "请输入狗A的 性别(0:母 1:公) 年龄 品种" << endl;
        cin >> sex >> age >> variety;
        Dog dogA(sex,age,variety);
        cout << "请输入狗B的 性别(0:母 1:公) 年龄 品种" << endl;
        cin >> sex >> age >> variety;
        Dog dogB(sex,age,variety);
        Dog* NewDog=dogB.brith(dogA);
        if(NewDog==NULL)
        {
            cout << "dogA和dogB无法生育" << endl;
            continue;
        }
        cout << "小狗的属性:" << endl;
        cout << "sex:" << NewDog->get_sex() << endl;
        cout << "age:" << NewDog->get_age() << endl;
        cout << "variety:" << NewDog->get_variety() << endl;
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1845719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树的这五种遍历方法你们都会了吗?

说在前面 &#x1f388;二叉树大家应该都很熟了吧&#xff0c;那二叉树的这五种遍历方式你们都会了吗&#xff1f; 以这一二叉树为例子&#xff0c;我们来看看不同遍历方式返回的结果都是怎样的。 前序遍历 前序遍历的顺序是&#xff1a;首先访问根节点&#xff0c;然后递归地…

Power BI 连接数据源/获取数据(Excel表、文件夹、数据库、网页等)

一、Power BI 支持的数据源类型 Power BI支持广泛的数据源类型&#xff0c;包括文件、数据库、云服务和其他来源。 文件&#xff1a;Excel、文本/CSV、XML、JSON、文件夹、PDF等。数据库&#xff1a;SQL Server, Oracle, IBM DB2, MySQL, PostgreSQL等。云服务&#xff1a;Azur…

怎么学习PMP才是最正确的?

每个人的学习方式各不相同&#xff0c;不能一概而论说某种学习方式就是错误的。学习方式并没有绝对的对错之分&#xff0c;只能说是否适合自己&#xff0c;是否能够达到预期的学习效果。并不是别人的学习方式就一定适合自己&#xff0c;也不是不适合自己的学习方式就一定是错误…

简单好用的远程软件推荐? 内网端口映射工具快解析

最近有一些小伙伴求推荐一款简单好用的远程软件&#xff0c;经过多方对比试用&#xff0c;还是推荐快解析。通过快解析内网端口映射&#xff0c;可以让主机电脑自带远程桌面连接直接提供跨网访问控制&#xff0c;外网访问端无需再安装客户端软件&#xff01; 1.目标服务器本地…

【QCustomPlot实战系列】QCPGraph堆叠图

将一个QCPGraph叠加到另一个QCPGraph上显示&#xff0c;就得到了折线堆叠图 static QCPScatterStyle GetScatterStyle(const QColor& color) {QPen pen(color, 2);return QCPScatterStyle(QCPScatterStyle::ssCircle,pen,Qt::white, 5); }static QCPGraph* AddGraph(QCus…

ADI Trinamic TMC2300 完美用于相机镜头的马达驱动芯片方案

TMC2300-LA是一款用于高达 1.2A RMS 的两相步进电机的低压驱动芯片&#xff0c;低电压的步进电机驱动器是用于电池供电的、空间和待机功率临界驱动器应用。它的静音驱动技术StealthChop™能够为便携式&#xff0c;家庭和办公应用程序的非窃听运动控制&#xff0c;确保安静&…

树莓派4B学习笔记11:PC端网线SSH连接树莓派_网线连接请求超时问题解决

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 今日学习使用网线连接树莓派&#xff0c;网线可以提供更…

优思学院|质量管理中如何应用5W1H或5W2H方法?

5W1H或者5W2H其实是一种"工作方式"&#xff0c;它不是"思考方法"&#xff0c;这种工作方式是通过回答“什么&#xff08;What&#xff09;&#xff1f;为什么&#xff08;Why&#xff09;&#xff1f;谁&#xff08;Who&#xff09;&#xff1f;何时&#…

Flutter【组件】富文本组件

简介 flutter 富文本组件。 github地址&#xff1a; https://github.com/ThinkerJack/jac_uikit 使用方式 运行 flutter pub add jac_uikit组件文档 使用方式&#xff1a; HighlightedTextWidget.builder(text: "全部文案包含高亮文案测试用",highlights: [Hig…

Rust高性能日志库tklog0.0.8—支持mod设置参数

tklog是rust高性能结构化日志库&#xff0c;支持同步日志&#xff0c;异步日志&#xff0c;支持自定义日志的输出格式&#xff0c;支持按时间&#xff0c;按文件大小分割日志文件&#xff0c;支持日志文件压缩备份&#xff0c;支持官方日志库标准API&#xff0c;支持mod独立参数…

控制台瞬间干净!Vue 删除生产环境烦人的console.log打印,看完这个你就会了!

1.安装插件 需要安装babel-plugin-transform-remove-console插件 yarn add babel-plugin-transform-remove-console //或者 npm install babel-plugin-transform-remove-console2.配置babel.config.js // 所有生产环境&#xff0c;或者也可以配置测试环境 const prodPlugin …

文心一言 VS 讯飞星火 VS chatgpt (285)-- 算法导论21.2 4题

四、请给出图 21-3 所示操作序列的一个运行时间的渐近紧确界&#xff0c;假定使用链表表示和加权合并启发式策略。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 图 21-3 没有直接给出&#xff0c;但通常这种操作序列可能与某种数据结构&#xff08;如并查集或…

计算机网络:网络层 - 虚拟专用网 VPN 网络地址转换 NAT

计算机网络&#xff1a;网络层 - 虚拟专用网 VPN & 网络地址转换 NAT 专用地址与全球地址虚拟专用网 VPN隧道技术 网络地址转换 NAT网络地址与端口号转换 NAPT 专用地址与全球地址 考虑到 IP 地址的紧缺&#xff0c;以及某些主机只需要和本机构内部的其他主机进行通信&…

【经验分享】Ubuntu 24.04 安装搜狗输入法(亲测有效)

【经验分享】Ubuntu 24.04 安装搜狗输入法&#xff08;亲测有效&#xff09; 先用如下Ubuntu22.04的安装方法进行安装 Ubuntu 22.04安装搜狗输入法 发现存在闪屏问题&#xff0c;那解决闪屏问题不就好了&#xff0c;解决方法如下&#xff1a; Ubuntu 23.10 搜狗拼音输入法闪屏…

OceanMind海睿思参与编写的《数据智能白皮书(2024年)》正式发布!

近日&#xff0c;由中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;牵头和组织&#xff0c;中新赛克海睿思作为TC601-WG16人工智能数据工作组合作伙伴参与编写的《数据智能白皮书&#xff08;2024年&#xff09;》正式发布。 中新赛克的卢云川、…

WebStorm 配置 PlantUML

1. 安装 PlantUML 插件 在 WebStorm 插件市场搜索 PlantUML Integration 并安装&#xff0c;重启 WebStorm 使插件生效。 2. 安装 Graphviz PlantUML 需要 Graphviz 来生成图形。使用 Homebrew 安装 Graphviz&#xff1a; 打开终端&#xff08;Terminal&#xff09;。确保你…

MySQL——索引(概述和结构介绍)

一、索引概述 1、索引(index&#xff09;是帮助 MySQL 高效获取数据的数据结构(是一种有序的数据结构)。 2、在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构。这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c;这样就可以在这些…

什么是联盟营销?独立站如何通过联盟营销提高转化率?

什么是联盟营销&#xff1f;独立站如何通过联盟营销提高转化率&#xff1f; 流量紧缺是跨境电商独立站永恒不变的难题&#xff0c;对于独立站卖家来说&#xff0c;广告投放、KOL引流等推广方式都能带来流量&#xff0c;但在广告流量越来越贵的今天&#xff0c;对于跨境电商独立…

构建一个强大的小型虚拟负载

您以前可能听说过“虚拟负载”一词&#xff0c;并想知道它的含义。 如果是这样&#xff0c;你并不孤单。虽然这个名字可能会让你了解它的作用&#xff0c;但它最初听起来可能令人困惑。 在这里&#xff0c;我们将深入探讨什么是虚拟负载&#xff0c;它们的作用以及如何选择适合…

王者荣耀图鉴皮肤怎么来的

王者荣耀图鉴皮肤怎么来的 最近一个王者荣耀图鉴开源很火 这个项目里面有很多的图片和音效资源&#xff0c;最简单的方法就是利用爬虫技术爬取这些图片资源。 第一步环境准备 Pyhton3.12macos系统 第二步查看王者荣耀官网 这些图片资源最简单的来源就是王者荣耀官网网站…