C++类与对象-六大成员函数

默认成员函数就是用户没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个空类编译器会默认⽣成8个默认成员函数。本文只介绍其中6个,C++11增加两个函数见后续博客。

目录

一、构造函数

1.1 概念

1.2 特性

1.3 使用举例

1.4 初始化列表

1.4.1 概念

1.4.2 特性

1.4.3 使用

二、析构函数

2.1 概念

2.2 特性

2.3 使用

三、拷贝构造

3.1 概念

3.2 特性

3.3 使用

四、赋值运算符重载

4.1 特性

4.2 使用

4.3 与拷贝构造的区别

五、取地址操作符重载

5.1 cosnt

5.2 重载

六、操作符重载补充

6.1 前置++与后置++

6.2 流操作符重载


一、构造函数

1.1 概念

构造函数是一个特殊的成员函数,名字与类名相同,不写返回值。创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。

1.2 特性

  1. 函数名与类名相同。⽆返回值。 (返回值啥都不需要给,也不需要写void)
  2. 构造函数可以重载。分为无参构造函数,带参构造函数,全缺省构造函数
    class Date
    {
    public:// 1. ⽆参构造函数Date(){_year = 1;_month = 1;_day = 1;}// 2. 带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}//3. 全缺省构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
    private:int _year;int _month;int _day;
    };
  3. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显式定义编译器将不再⽣成。
  4. 默认构造函数:⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数。这三个函数有且只有⼀个存在,不能同时存在。不传实参就可以调⽤的构造就叫默认构造。
  5. 用户不写,编译器默认⽣成的构造,对内置类型成员变量的初始化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错

1.3 使用举例

#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1; // 调⽤默认构造函数Date d2(2005, 6, 8); // 调⽤带参的构造函数d1.Print();d2.Print();return 0;
}

1.4 初始化列表

1.4.1 概念

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值表达式

class Date
{
public:
Date(int year, int month, int day): _year(year), _month(month), _day(1)
{}
private:int _year;int _month;int _day;
};

 如何理解初始化列表?

借助C语言的特性:在private中可以看作是变量的声明,如果用之前在函数体中的构造,我们可以发现缺少了变量的定义。事实上如果我们不写初始化列表,编译器会自动生成初始化列表。

由此可知,初始化列表其实是每个成员变量定义初始化的地⽅

为什么会有初始化列表?

有些变量比如:const变量,还有引用----要求在定义时初始化,仅仅依靠之前在函数体内初始化的方式是无法对这些变量进行初始化的,所以初始化列表至关重要。

1.4.2 特性

  1. 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错。
  2. C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的成员使⽤的。
  3. 在初始化列表的成员-显式写;不在初始化列表的成员:a.声明的地方有缺省值用缺省值 b.没有缺省值:内置类型看编译器处理,自定义类型调用默认构造。如果没有默认构造会报错。
  4. 初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。
    /*试分析下列程序*/
    #include<iostream>
    using namespace std;
    class test
    {
    public:test(int a):_t1(a), _t2(_t1){}void Print() {cout << _t1 << " " << _t2 << endl;}
    private:int _t2 = 2;int _t1 = 2;
    };
    int main()
    {test t(1);t.Print();
    }

    由于初始化列表是按照声明顺序初始化的,先初始化_t2,此时_t1没有初始化为随机值,所以结果如下

#include<iostream>
using namespace std;
class Test
{
public:Test(int a):_t(a){}
private:int _t;
};
class Date
{
public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day)/*, _t(12), _ref(x), _n(1)*/{}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;Test _t; // 没有默认构造int& _ref; // 引⽤const int _n; // const
};
int main()
{int i = 0;Date d1(i);d1.Print();return 0;
}

1.4.3 使用

由于每个构造函数都有初始化列表,建议以后都采用初始化列表的方式进行构造函数的初始化,使用中建议建议声明顺序和初始化列表顺序保持⼀致。

二、析构函数

2.1 概念

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

2.2 特性

  1. 析构函数名是在类名前加上字符~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 析构函数不能重载。编译器自动调用
  5. 不写编译器会自动生成默认析构函数

2.3 使用

  1. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
    Date类;
  2. 有资源申请时,必须要写,否则会造成资源泄漏,比如Stack类。
#include<iostream>
using namespace std;class Stack
{
public:Stack(size_t capacity = 3){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = _capacity;_size = 0;}~Stack(){if (_arr){free(_arr);_arr = NULL;_capacity = _size = 0;}}private:int* _arr;int _capacity;int _size;
};

三、拷贝构造

3.1 概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存
在的类类型对象创建新对象时由编译器自动调用。

为什么必须是引用?

由C语言相关知识我们可以知道:传值调用实际上是传了当前变量的拷贝。所以如果拷贝构造函数的参数为传值调用,会引发无限的调用递归。所以要用传引用调用

3.2 特性

  1. 拷⻉构造函数是构造函数的⼀个重载。
  2. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返回都会调⽤拷⻉构造完成。
  3. 若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。

浅拷贝:默认的浅拷贝仅复制指针的地址,两个对象共享同一块动态内存。如果一个对象释放了内存,另一个对象会出现悬空指针的问题。适用于对象不包含动态内存或对动态内存的共享是可接受的情况。
深拷贝:通过手动实现深拷贝构造函数和赋值运算符,确保每个对象都有自己独立的动态内存,避免悬空指针和共享内存的问题。适用于对象包含动态内存且需要独立内存空间的情况。

3.3 使用

有以下三种情况

  1. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉,所以不需要我们显⽰实现拷⻉构造。
  2. 像Stack这样的类,虽然也都是内置类型,但是指向了资源,编译器⾃动⽣成的拷⻉构造完成的浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉
  3. 像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器自动生成的拷贝构造会调⽤Stack的拷⻉构造,也不需要我们显⽰实现MyQueue的拷⻉构造。
#include<iostream>
using namespace std;class Stack
{
public:Stack(size_t capacity = 3){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = _capacity;_size = 0;}~Stack(){if (_arr){free(_arr);_arr = NULL;_capacity = _size = 0;}}Stack(const Stack& s){_arr = (int*)malloc(sizeof(int) * s._capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = s._capacity;_size = s._size;memcpy(_arr, s._arr, sizeof(int) * s._size);}private:int* _arr;int _capacity;int _size;
};

注意事项:

传值返回会产⽣⼀个临时对象调⽤拷⻉构造。

传值引⽤返回,返回的是返回对象的别名(引⽤),没有产⽣拷⻉。

但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少拷⻉,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。

四、赋值运算符重载

4.1 特性

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引⽤,否则会传值传参会有拷⻉。
  2. 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了支持连续赋值场景。
  3. 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认构造函数类似,对内置类型成员变量会完成浅拷⻉,对⾃定义类型成员变量会调⽤他的拷⻉构造。

4.2 使用

与拷贝构造函数使用相同,分为三类

  1. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的赋值运算符重载就可以完成需要的拷⻉,所以不需要我们显⽰实现赋值运算符重载。
  2. 像Stack这样的类,虽然也都是内置类型,但是指向了资源,编译器⾃动⽣成的赋值运算符重载完成的浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉。
  3. 像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器⾃动⽣成的赋值运算符重载会调⽤Stack的赋值运算符重载,也不需要我们显⽰实现MyQueue的赋值运算符重载。
#include<iostream>
using namespace std;class Stack
{
public:Stack(size_t capacity = 3){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = _capacity;_size = 0;}~Stack(){if (_arr){free(_arr);_arr = NULL;_capacity = _size = 0;}}Stack(const Stack& s){_arr = (int*)malloc(sizeof(int) * s._capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = s._capacity;_size = s._size;memcpy(_arr, s._arr, sizeof(int) * s._size);}Stack& operator=(const Stack& s){//检查是否自己给自己赋值if (this != &s){_arr = (int*)malloc(sizeof(int) * s._capacity);if (_arr == NULL){perror("malloc");exit(1);}_capacity = s._capacity;_size = s._size;memcpy(_arr, s._arr, sizeof(int) * s._size);}}private:int* _arr;int _capacity;int _size;
};
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}// d1 = d2 表达式的返回对象应该为d1 ,也就是*thisreturn *this;}
private:int _year;int _month;int _day;
};

4.3 与拷贝构造的区别

  1. 赋值重载完成两个已经存在的对象直接的拷⻉赋值
  2. 拷⻉构适用于⼀个对象拷⻉初始化给另⼀个要创建的对象(还没创建)
int main()
{Date d1(2024, 7, 5);Date d2(d1);//拷贝构造Date d3(2024, 7, 6);d1 = d3;//赋值运算符重载Date d4 = d1;//拷贝构造return 0;
}

五、取地址操作符重载

5.1 cosnt

  • 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。
  • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。
    //以Date类为例
    //this指针由 Date* const this 变为 const Date* const this
    class Date
    {
    public:void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
    private:int _year;int _month;int _day;
    };

5.2 重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动⽣成的就足够使用了,不需要去显⽰实现。

六、操作符重载补充

具体应用实现详见:

6.1 前置++与后置++

重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。所以C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。

6.2 流操作符重载

重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。

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

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

相关文章

如何通过前端表格控件实现自动化报表?

背景 最近伙伴客户的项目经理遇见一个问题&#xff0c;他们在给甲方做自动化报表工具&#xff0c;项目已经基本做好了&#xff0c;但拿给最终甲方&#xff0c;业务人员不太买账&#xff0c;项目经理为此也是天天抓狂&#xff0c;没有想到合适的应对方案。 现阶段主要面临的问…

docker笔记7-dockerfile

docker笔记7-dockerfile 一、dockerfile介绍二、dockerfile指令三、构建自己的镜像 一、dockerfile介绍 Dockerfile是用来构建Docker镜像的构建文件&#xff0c;是由一系列命令和参数构成的脚本。 以下是常用的 Dockerfile 关键字的完整列表和说明&#xff1a; 二、docker…

【计算机毕业设计】838装修公司CRM系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

为什么要做边界值测试?

边界值测试的理解 边界值测试&#xff08;Boundary Value Testing&#xff09;是一种常用的软件测试方法&#xff0c;它侧重于测试输入值的边缘或临界条件。这些边缘条件通常包括最小值、最大值以及接近这些最小值和最大值的值。边界值测试的基本思想是&#xff0c;许多软件错…

React 学习——Context机制层级组件通信

核心思路&#xff1a;&#xff08;适用于所有层级&#xff0c;不仅仅是爷孙 父子&#xff09; createContext方法创建一个上下文对象在顶层组件 通过Provider组件提供数据在底层组件&#xff0c;通过useContext钩子函数使用数据 import { createContext, useContext } from …

R语言 爬取数据+简单清洗

小小练习。见代码注释 # 加载必要的包 library(rvest) library(dplyr) library(tidyr)# 指定网页URL url <- "https://research.un.org/en/unmembers/scmembers"# 读取网页内容 webpage <- read_html(url)# 提取所有表格节点 table_nodes <- html_nodes(web…

基于人工智能及大数据的综合智能交通管理平台(可编辑30页PPT)

引言&#xff1a;随着城市化进程的加速和汽车保有量的快速增长&#xff0c;交通拥堵、交通事故频发以及交通资源分配不均等问题日益突出&#xff0c;成为制约城市发展的重要因素。为了应对这些挑战&#xff0c;基于人工智能&#xff08;AI&#xff09;及大数据技术的综合智能交…

【React】详解自定义 Hook

文章目录 一、自定义 Hook 的基本用法1. 什么是自定义 Hook&#xff1f;2. 创建自定义 Hook3. 使用自定义 Hook 二、自定义 Hook 的进阶应用1. 处理副作用2. 组合多个 Hook3. 参数化 Hook4. 条件逻辑 三、自定义 Hook 的实际应用案例1. 实现用户身份验证2. 实现媒体查询 四、最…

民大食堂用餐小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;档口号管理&#xff0c;商家餐品管理&#xff0c;餐品种类管理&#xff0c;购物车管理&#xff0c;订单信息管理 微信端账号功能包括&#xff1a;系统首页&a…

angular入门基础教程(七)系统路由

路由的实现 当我们系统越来复杂&#xff0c;功能越来越多&#xff0c;路由也就是必须的了。在 ng 中如何实现路由呢&#xff1f; 启用路由 在 app 目录下&#xff0c;新建一个 router 目录&#xff0c;把 app.routers.ts 文件拷贝过来&#xff0c;并修改一下。 import { Ro…

C语言程序设计16

程序设计16 问题16_1代码16_1结果16_1 问题16_2代码16_2结果16_2 问题16_3代码16_3结果16_3 问题16_1 函数 f u n fun fun 的功能是&#xff1a;逆置数组元素中的值。 例如&#xff0c;若形参 a a a 所指数组中的数据最初排列为 &#xff1a; 1 , 2 , 3 , 4 , 5 , 6 …

高职院校大数据人才培养成果导向系统构建、实施要点与评量方法

一、引言 在当今信息化快速发展的背景下&#xff0c;大数据已成为推动社会进步和产业升级的重要力量。为满足社会对大数据人才的需求&#xff0c;高职院校纷纷开设大数据相关专业&#xff0c;并致力于探索科学有效的人才培养模式。本文立足于我国信息化与智能化发展趋势&#…

【初阶数据结构】10.排序(1)

文章目录 1.排序概念及运用1.1 概念1.2 运用1.3 常见排序算法 2. 实现常见排序算法2.1 插入排序2.1.1 直接插入排序2.1.2 希尔排序2.1.2.1 希尔排序的时间复杂度计算 2.2 选择排序2.2.1 直接选择排序2.2.2 堆排序 1.排序概念及运用 1.1 概念 排序&#xff1a;所谓排序&#x…

如何用PostMan按照规律进行循环访问接口

①设置动态变量 步骤一: 设置环境变量 1. 创建环境变量集合 在 Postman 左上角选择 "环境"&#xff0c;然后点击 "添加" 来创建一个新的环境变量集合。给它起一个名称&#xff0c;比如 "uploadDemo". 2. 添加初始变量 在新创建的环境变量集…

基于python的百度迁徙迁入、迁出数据分析(三)

百度迁徙定义 百度迁徙释义&#xff1a; 百度迁徙以用户常住地所在地市或停留超过一天的非常住地定义为出发城市&#xff0c;以用户离开出发城市&#xff0c;并在非出发城市停留超过4 h以上定义为到达城市。采用4h阈值&#xff0c;排除了城际出行中的途经地。 定义参考来源…

filament 初使用记录

安装初始化 一、环境准备 官网要的 我安装的 二、下载安装 安装laravel composer create-project --prefer-dist laravel/laravel 项目名称 10.*导入 filament composer require filament/filament注册 filament 管理面板 php artisan filament:install --panels初始化…

freertos-HAL库-STM32Cubemax生成

打开cubemax选好型号配置RCC&#xff08;外部高速时钟&#xff09;这里查看原理图&#xff0c;我们把按键设为输入&#xff0c;led设为输出创建两个新任务&#xff08;default是系统创建的&#xff09;配置时钟&#xff0c;这里HSE是外部高速时钟&#xff0c;HSI是内部的&#…

axure10的安装与使用教程,问题整理

前言&#xff1a; axure10的安装与激活使用教程。 1、百度网盘下载相关资料 链接&#xff1a;https://pan.baidu.com/s/1OSD9J1wVuIptGxeRzwjlpA?pwddkbj 提取码&#xff1a;dkbj 2、开始安装&#xff0c;点击setup的安装包 除了更改地址外&#xff0c;其他的默认就行&…

Matlab编程资源库(15)数值积分

一、基本原理 求解定积分的数值方法多种多样&#xff0c;如简单的梯形法、辛普生(Simpson)法、牛顿&#xff0d;柯特斯(Newton-Cotes)法等都是经常采用的方法。它们的基本思想都是将整个积分区间[a,b]分成n个子区间[xi,xi1] &#xff0c;i1,2,…,n&#xff0c;其中 x 1a&#…

2024年PINN网络​还在火!发论文侧重点在哪儿?

2024年了&#xff0c;PINN网络依然火爆&#xff0c;各大顶会顶刊都能看见它的相关论文。 这是因为&#xff0c;AI交叉学科通常离不开求解偏微分方程PDE&#xff0c;而传统的求解方法受初始假设限制&#xff0c;一旦没设好就会导致很大的误差。 PINN作为一种新的思路&#xff…