C++面向对象基础-构造函数

1、构造函数

1.1 基本使用

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

  • 函数名必须与类名完全相同
  • 构造函数不写返回值
  • 如果程序员不手动编写构造函数,编译器就会自动添加一个无参的构造函数

手动添加构造函数,编译器就不会自动添加构造函数。

    • 构造函数在创建对象时,常用于给对象的属性赋予初始值。

#include <iostream>

using namespace std;

// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量

public: // 权限:public是最开放的权限。

    MobilePhone() // 默认构造函数
    {
        brand = "8848";
        modle = "M6 巅峰版";
        weight = 500;
    }
    // 品牌的get函数
    string get_brand()
    {
        return brand;
    }
    // 型号的get函数
    string get_modle()
    {
        return modle;
    }
    // 重量的get函数
    int get_weight()
    {
        return weight;
    }
    // 品牌的set函数
    void set_brand(string b)
    {
        brand = b;
    }
};
int main()
{
    // 栈内存对象
    MobilePhone mp1;
    cout << mp1.get_brand() << endl;
    cout << mp1.get_modle() << endl;
    cout << mp1.get_weight() << endl;
    return 0;
}

    • 构造函数也支持函数重载和函数参数默认值

#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:public是最开放的权限。
    MobilePhone() // 默认构造函数
    {
        brand = "8848";
        modle = "M6 巅峰版";
        weight = 500;
    }
    // 有参构造函数(函数重载)
    MobilePhone(string b,string m,int w)
    {
        brand = b;
        modle = m;
        weight = w;
    }
    // 品牌的get函数
    string get_brand()
    {
        return brand;
    }

    // 型号的get函数
    string get_modle()
    {
        return modle;
    }

    // 重量的get函数
    int get_weight()
    {
        return weight;
    }
    // 品牌的set函数
    void set_brand(string b)
    {
        brand = b;
    }
};
int main()
{
    // 栈内存对象
    MobilePhone mp1("vivo","iqoo12",300);
    cout << mp1.get_brand() << endl;
    cout << mp1.get_modle() << endl;
    cout << mp1.get_weight() << endl;
    return 0;
}

在创建对象的时候,必须调用任意构造函数。

    • 函数参数默认值(全缺省默认构造函数)
  • 缺省部分往前写

#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:public是最开放的权限。
    MobilePhone() // 默认构造函数
    {
        brand = "8848";
        modle = "M6 巅峰版";
        weight = 500;
    }
    // 有参构造函数
    MobilePhone(string b,string m,int w)
    {
        brand = b;
        modle = m;
        weight = w;
    }
    MobilePhone(int w,string b = "oppo",string m = "findx")
    {
        brand = b;
        modle = m;
        weight = w;
    }
    // 品牌的get函数
    string get_brand()
    {
        return brand;
    }

    // 型号的get函数
    string get_modle()
    {
        return modle;
    }

    // 重量的get函数
    int get_weight()
    {
        return weight;
    }

    // 品牌的set函数
    void set_brand(string b)
    {
        brand = b;
    }
};


int main()
{
    // 栈内存对象
    MobilePhone mp1;
    cout << mp1.get_brand() << endl;
    cout << mp1.get_modle() << endl;
    cout << mp1.get_weight() << endl;

    MobilePhone *mp2 = new MobilePhone();

    return 0;
}

1.2 构造初始化列表

构造初始化列表是一种更简单的给成员变量赋予初始值的写法。传参遵寻就近原则。

注意:当构造函数的局部变量与成员变量同名时,除了使用后面学习的this指针外,也可以使用构造初始化列表的方式进行区分。

#include <iostream>
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
    string brand;   // 品牌
    string modle;   // 型号
    int weight;     // 重量
public: // 权限:public是最开放的权限。
    // 构造初始化列表:编译器成员变量与局部变量可以做区分
    MobilePhone()://直接赋值,成员变量已经创建。
        brand("8848"),modle("M6 巅峰版"),weight(500){}
    MobilePhone(string brand,string modle,int weight)://默认传参
        brand(brand),modle(modle),weight(weight){}
            //对象创造之前被调用,还没有创造对象,成员变量已经创建。不能放到函数体内,函数无法识别局部变量与成员变量
    // 品牌的get函数
    string get_brand()
    {
        return brand;
    }
    // 型号的get函数
    string get_modle()
    {
        return modle;
    }
    // 重量的get函数
    int get_weight()
    {
        return weight;
    }
};
int main()
{
    // 栈内存对象
    MobilePhone mp1;
    cout << mp1.get_brand() << endl;
    cout << mp1.get_modle() << endl;
    cout << mp1.get_weight() << endl;

    MobilePhone *mp2 = new MobilePhone("三星","note7",100);
    cout << mp2->get_brand() << endl;
    cout << mp2->get_modle() << endl;
    cout << mp2->get_weight() << endl;

    delete mp2;
    mp2 = NULL;
    return 0;
}

1.3 隐式调用构造函数

截至到目前为止,对于构造函数的调用都是显式调用的。

#include <iostream>

using namespace std;

class Test
{
private:
    int val;
public:
    Test(int v,int i,string str,double d)
    {
        val = v;
        cout << "构造函数" << endl;
    }

    int get_val()
    {
        cout << &val << endl;
        return val;
    }

};

int main()
{
//    Test t1(2);  // 显式调用构造函数
//    cout << t1.get_val() << endl;
    Test t2 = {123,2,"string",2.4}; // 隐式调用构造函数,多参数传递C++11支持
    cout << t2.get_val() << endl;

    return 0;
}


建议使用隐式调用,可以使用explicit关键字屏蔽隐式调用语法。

1.4 拷贝构造函数

1.4.1 概念

当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象创建可以通过这个构造函数进行实现。

#include <iostream>

using namespace std;

class Test
{
private:
    int val;
public:
    Test(int v)
    {
        val = v;
        cout << "构造函数" << endl;
    }
    // 手动添加默认的拷贝构造函数
    Test(Test &t)
    {
        val = t.val;
    }

    int get_val()
    {
        return val;
    }

};

int main()
{
    Test t1(4);
    cout << t1.get_val() << endl;

    Test t2(t1); // 调用拷贝构造函数
    cout << t2.get_val() << endl;

    return 0;
}

没有任何构造函数的时候:编译器会给我们创建一个默认构造函数、拷贝构造函数。

如果我们有一个默认构造函数,编译器不会创建默认构造函数,会创建拷贝构造函数。

如果我们有一个有参构造函数,编译器不会创建默认构造函数,会创建拷贝构造函数。

如果我们有一个拷贝构造函数,编译器不会创建默认构造函数,不会创建拷贝构造函数。

【思考】:拷贝构造函数会存在隐患吗?

存在,当成员变量出现指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指向同一处,改变一处另一处也会改变不符合面向对象的设计规范,这种现象被称为”浅拷贝“。

1.4.2 浅拷贝

存在的问题:更改一处三处都会改变

#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
    char *name;
public:
    Dog(char *n)
    {
        name = n;
    }
    void show_name()
    {
        cout << name << endl;
    }
};
int main()
{
    char arr[20] = "旺财";
    Dog d1(arr);
    Dog d2(d1); // 调用的是拷贝构造函数,不调用构造函数
    strcpy(arr,"大黄");//两个都会改变
    d1.show_name(); // 大黄
    d2.show_name(); // 大黄
    return 0;
}

这种情况必须要手写构造函数,使每次赋值都创建一个新的副本,从而每个对象单独持有自己的成员变量,这种方式被称为“深拷贝”。

1.4.3 深拷贝

new开辟的空间无法释放,造成内存泄漏的问题。

#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
    char *name;
public:
    Dog(char *n)
    {
        name = new char[20];
        strcpy(name,n);
    }
    Dog(Dog &d)
    {
       name = new char[20];
       strcpy(name,d.name);
    }
    void show_name()
    {
        cout << name << endl;
    }
};
int main()
{
    char arr[20] = "旺财";
    Dog d1(arr);
    Dog d2(d1); // 拷贝构造函数

    strcpy(arr,"大黄");

    d1.show_name(); // 旺财
    d2.show_name(); // 旺财

    return 0;
}

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

class Dog
{
private:
    char *name;
public:
    // 构造函数,初始化对象的name成员变量
    Dog(char *n)
    {
        name = n; // 将传入的指针赋值给name,这会导致问题后面会解释
    }
    // 拷贝构造函数,用于创建一个新的对象并复制已有对象的数据
    Dog(Dog &d)
    {
        name = new char[20]; // 为name分配内存空间
        strcpy(name, d.name); // 复制参数对象的name值到当前对象的name
    }
    // 打印狗的名字
    void show_name()
    {
        cout << name << endl;
    }
};

int main()
{
    char arr[20] = "旺财"; // 创建一个字符数组,并存储字符串"旺财"
    Dog d1(arr); // 创建一个Dog对象d1,传入arr作为参数
    
    Dog d2(d1); // 使用拷贝构造函数,创建一个新的Dog对象d2,复制d1的数据
    
    strcpy(arr, "大黄"); // 修改arr中的字符串为"大黄"
    
    d1.show_name(); // 输出:旺财,因为d1的构造函数只是将name指针指向arr的地址,所以修改arr后,d1的name也改变了
    
    d2.show_name(); // 输出:旺财,因为拷贝构造函数在创建d2时复制了d1的name,所以不受arr的修改影响
    
    return 0;
}

  1. 创建有参构造函数,编译器不会创建无参构造函数,会创建拷贝构造函数。
  2. 创建无参构造函数,编译器不会创建无参构造函数,会创建拷贝构造函数。
  3. 创建拷贝构造函数,编译器不会创建无参构造函数,不会创建拷贝构造函数。

【思考】 深拷贝的代码是否存在隐患?

存在,new开辟的空间无法释放,造成内存泄漏的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小白学 PyTorch 系列:54个超强 pytorch 操作

最近观察到一个有趣的趋势&#xff0c;越来越多的人在学术界热衷于学习和应用PyTorch。在工业界&#xff0c;虽然仍有一些项目在延续使用之前的深度学习框架&#xff0c;但 PyTorch 的影响力也在逐渐渗透。 对于昨天为什么没发文&#xff0c;原因很心酸。把 PyTorch 的这篇文章…

亚马逊鲲鹏系统一款自动化全能软件

亚马逊鲲鹏系统是一款专为亚马逊买家提供全方位功能的自动化软件。它不仅可以轻松实现自动注册、养号、测评、QA等一系列操作&#xff0c;更在用户关心的账号关联问题上做出了创新性的解决方案。有的朋友可能对全自动化操作心存疑虑&#xff0c;担心可能引起关联从而导致封号&a…

24届春招实习必备技能(一)之MyBatis Plus入门实践详解

MyBatis Plus入门实践详解 一、什么是MyBatis Plus? MyBatis Plus简称MP&#xff0c;是mybatis的增强工具&#xff0c;旨在增强&#xff0c;不做改变。MyBatis Plus内置了内置通用 Mapper、通用 Service&#xff0c;仅仅通过少量配置即可实现单表大部分 CRUD 操作&#xff0…

FileZilla的使用,主动模式和被动模式思维导图

注&#xff1a;图片 &#xff08;与上面的思维导图文字配图看&#xff09;

PAT乙级1045 快速排序

著名的快速排序算法里有一个经典的划分过程&#xff1a;我们通常采用某种方法取一个元素作为主元&#xff0c;通过交换&#xff0c;把比主元小的元素放到它的左边&#xff0c;比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列&#xff0c;请问有多少个元…

node版本管理器nvm的下载和使用

介绍 nvm 全名 node.js version management&#xff0c;顾名思义是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs。 下载和安装 在下载和安装nvm前&#xff0c;需要确保当前电脑没有安装node&#xff0c;否则则需要先把原来的node卸载了。 下载地址&#…

Oracle-深入了解cache buffer chain

文章目录 1.Cache buffer chain介绍2.Buffer cache的工作原理3 Buffer chains4.Multi-versioning of Buffers5.Latches6.诊断CBC latch等待7.解决 CBC Latch等待 1.Cache buffer chain介绍 经常看到会话等待事件“latch&#xff1a;cache buffers chain”。 如果想知道意味着什…

007、控制流

先看下本篇学习内容&#xff1a; 通过条件来执行 或 重复执行某些代码 是大部分编程语言的基础组成部分。在Rust中用来控制程序执行流的结构主要就是 if表达式 与 循环表达式。 1. if表达式 if表达式允许我们根据条件执行不同的代码分支。我们提供一个条件&#xff0c;并且做出…

Reac03:react脚手架配置(代理配置)

react脚手架配置 reactAjax下载Axios配置代理第二种配置代理的方式 github搜索案例 reactAjax React本身只关注于界面&#xff0c;并不包含发送ajax请求的代码前端应用需要通过ajax请求与后台进行交互(json数据)react应用中需要集成第三方ajax(或自己封装) 常用的ajax请求库 j…

ctfshow——文件上传

文章目录 文件上传思路web 151web 152web 153知识点解题 web 154web 155web 156web 157web 158web 159web160web 161 文件上传思路 web 151 打开页面显示&#xff1a;前台校验不可靠。说明这题是前端验证。 右键查看源代码&#xff0c;找到与上传点有关的前端代码&#xff1a…

[SSD 测试 1.3] 消费级SSD全生命周期测试

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解SSD》 <<<< 返回总目录 <<<< 构建消费级SSD全生命周期测试,开展性能测试、兼容性测试、功能测试、环境应力测试、可靠性测试、电器检测。 以忆联消费级存储实验室为例,消费级存储实验室面积…

docker应用部署(部署MySql,部署Tomcat,部署Nginx,部署Redis)

Docker 应用部署 一、部署MySQL 搜索mysql镜像 docker search mysql拉取mysql镜像 docker pull mysql:5.6创建容器&#xff0c;设置端口映射、目录映射 # 在/root目录下创建mysql目录用于存储mysql数据信息 mkdir ~/mysql cd ~/mysqldocker run -id \ -p 3307:3306 \ --na…

信号与线性系统翻转课堂笔记19——连续/离散系统的零极点与稳定性

信号与线性系统翻转课堂笔记19——连续/离散系统的零极点与稳定性 The Flipped Classroom19 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#x…

中科亿海微UART协议

引言 在现代数字系统设计中&#xff0c;通信是一个至关重要的方面。而UART&#xff08;通用异步接收器/发送器&#xff09;协议作为一种常见的串行通信协议&#xff0c;被广泛应用于各种数字系统中。FPGA&#xff08;现场可编程门阵列&#xff09;作为一种灵活可编程的硬件平台…

2023结婚成家,2024借势起飞

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

《深入理解JAVA虚拟机笔记》Java 运行时内存区域

程序计数器&#xff08;线程私有&#xff09; 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看做是当前线程所执行的字节码的行号指示器。在 Java 虚拟机的概念模型里&#xff0c; 字节码解释器工作时就是通过改变这个计…

解决npm,pnpm,yarn等安装electron超时等问题

我在安装electron的时候&#xff0c;出现了超时等等各种问题&#xff1a; &#xff08;RequestError: connect ETIMEDOUT 20.205.243.166:443&#xff09; npm yarn&#xff1a;Request Error: connect ETIMEDOUT 20.205.243.166:443 RequestError: socket hang up npm ER…

2022年山东省职业院校技能大赛高职组云计算赛项试卷第二场-容器云

2022年山东省职业院校技能大赛高职组云计算赛项试卷 目录 【赛程名称】云计算赛项第二场-容器云 需要竞赛软件包以及资料可以私信博主&#xff01; 【赛程名称】云计算赛项第二场-容器云 【赛程时间】2022-11-27 09:00:00至2022-11-27 16:00:00 说明&#xff1a;完成本任务…

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存?

LRU&#xff08;Least Recently Used&#xff09;缓存是一种常用的缓存淘汰策略&#xff0c;用于在有限的缓存空间中存储数据。其基本思想是&#xff1a;如果数据最近被访问过&#xff0c;那么在未来它被访问的概率也更高。因此&#xff0c;LRU缓存会保留最近访问过的数据&…

23种设计模式Python版

目录 创建型模式简单工厂模式工厂方法模式抽象工厂模式单例模式原型模式建造者模式 结构型模式适配器模式桥接模式组合模式装饰器模式外观模式享元模式代理模式 行为型模式职责链模式命令模式解释器模式迭代器模式中介者模式备忘录模式观察者模式状态模式策略模式模板方法模式访…