Cpp04 — 默认成员函数

news2024/11/17 3:29:22

前言:本文章主要用于个人复习,追求简洁,感谢大家的参考、交流和搬运,后续可能会继续修改和完善。

因为是个人复习,会有部分压缩和省略。

 一、默认成员函数

当类里面成员函数什么都不写的时候,编译器会自动生成6个默认成员函数

六个成员函数包括:

构造函数(主要完成初始化工作)

析构函数(主要完成清理工作)

拷贝构造(视同同类对象初始化创建对象)

赋值重载(主要是把一个对象赋值给另一个对象)

取地址重载(主要是普通对象和const对象取地址,这两个很少需要自己实现)

默认成员函数对于内置类型成员不处理,对于自定义类型成员,它会去调用它的构造函数、析构函数。我们主要探讨前四个

1.构造函数(主要完成初始化工作)

因为我们有时候可能会忘记调用初始化函数,C++为了解决这个问题,引入了构造函数来初始化,构造函数不可能没有调用,它会在对象实例化时自动调用,保证对象一定有初始化流程。

构造函数的特点:

1.函数名与类名相同

2.无返回值

3.对象实例化时编译器自动调用对应的构造函数(保证了对象一定会初始化)

4.构造函数可以重载(我们就可以有多种初始化方式)

 给一个全缺省的初始化是最好的

注意:调试的时候,只能看当前作用域里的变量

当全缺省构造函数和无任何参数的构造函数同时存在时,编译会出错,因为无法判断是要调用哪一个构造函数。语法上是可以的,但是实际使用时是不可以的。

我们不写,编译器会生成一个无参的构造函数,我们写了编译器就不会生成了。所以说构造函数是默认构造函数。虽然构造函数默认生成了,但是其初始化时,不会把值初始为0,而是随机值

对于内置类型(基本类型)语言原生定义的类型,如char、int、double、指针等等编译器不会初始化为0。对于自定义类型:class、struct等定义的类型,编译器会去调用它们的默认构造函数初始化为0。构造函数还是自己写靠谱,绝大多数情况下,编译器生成的默认构造函数并不好

默认构造函数:我们不写,编译器自动生成,我们写了,编译器自动生成。这个理解有一些地方不对。我们在写构造函数时,最好写不用传参就可以调用的构造函数,全缺省的构造函数是最好的,它可以适应各种场景。

1.我们不写,编译器默认生成的

2.我们自己写的无参的

编译器默认生成的并不是什么都不做,而是有区分的。

class Date
{
    private:
        int _year;
        int _month;
        int _day;
};

int main()
{
    Date d1(2022,5,15);//可以
    Date d2;//可以
    Date d3();//不可以,未调用原型函数。没有调用到构造函数,对象没有被构造出来
    return 0;
}

 那么我们如何验证呢?如下:

class Date
{
    public:
        void Print()
        {
            cout << "cout" << endl;
        }
    private:
        int _year;
        int _month;
        int _day;
};

int main()
{
    Date d1(2022,5,15);//可以
    Date d2;//可以
    Date d3();//不可以,未调用原型函数。没有调用到构造函数,对象没有被构造出来
    d1.Print();
    d2.Print();
    d3.Print();//这里会出错,会显示"Print"的左边必须有类/结构/联合
    return 0;
}

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

虽然有些构造函数调用之后,对象已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,不能称作初始化,因为初始化只能初始化一次,而构造函数体内可以多次赋值。

 

time*

初始化列表

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

class Date

{

public:

Date(int year, int month, int day)

: _year(year)

, _month(month)

, _day(day)

{}

private:

int _year;

int _month;

int _day;

};

注意:

        1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

        2.类中包含以下成员,必须放在初始化列表位置进行初始化:

                引用成员变量、const成员变量、自定义类型成员(该类没有默认构造函数)

        3.尽量使用初始化列表初始化,因为不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

        4.成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

class A

{

public:

A(int a)

:_a1(a)

,_a2(_a1)

{}

void Print() {

cout<<_a1<<" "<<_a2<<endl;

}

private:

int _a2;

int _a1;

}

int main() {

A aa(1);

aa.Print();

}

A. 输出1 1

B.程序崩溃

C.编译不通过

D.输出1 随机值

选D

explicit关键字 

构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换地作用

class Date

{

public:

Date(int year)

:_year(year)

{}

explicit Date(int year)

:_year(year)

{}

private:

int _year;

int _month:

int _day;

};

void TestDate()

{

Date d1(2018);

// 用一个整形变量给日期类型对象赋值

// 实际编译器背后会用2019构造一个无名对象,最后用无名对象给d1对象进行赋值

d1 = 2019;

}

上述代码可读性不是很好,用explicit修饰构造函数,将会禁止单参构造函数的隐式转换

2.析构函数(主要完成清理工作)

对象的构造和销毁是编译器干的。构函数的作用是对于资源的清理,会在对象销毁时(生命周期到时)自动调用

析构函数的特性:

1.析构函数需要在类名前加上~

2.没有参数没有返回值(无法重载)

3.一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数

4.对象生命周期结束时,C++编译系统自动调用析构函数

有些类的析构函数才有意义,例如类中有malloc的。

关于调用顺序

因为对象是定义在函数中的,函数调用会建立栈帧,栈帧中的对象构造和析构也要符合后进先出

3.拷贝构造函数(使用同类对象初始化创建对象)

拷贝构造函数在我们不写时会自动生成,会对内置类型完成浅拷贝或者值拷贝

Date d4(d1);

这样之后d4和d1的值就是一样的。即使用d1的值初始化创建出来的d4.

拷贝构造也是一种构造函数

其特征如下:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个必须使用引用传参,使用传值方式会引发无穷递归调用。

也就是说这样写是不对的:

Date(Date d)
{
    _year = year;
    _month = month;
    _day = day;
}

 非法的拷贝构造函数,要把传参括号里的Date d写成Date& d,不然传值会一直进行拷贝构造,这里先省一个图。为了避免这个,我们有两种解决方式:1.传引用2.用指针

这里用指针比较麻烦,推荐使用传引用

拷贝构造建议加const,这样写反了才能报错

Date(const Date& d)
{
    _year = year;
    _month = month;
    _day = day;
}

Date d4(d1);

如果拷贝构造了一块空间,会出很多错误

 那么拷贝构造函数对于自定义类型呢?

自定义类型会调用它自己的拷贝构造

对于浅拷贝,默认生成的拷贝构造就够用了,像Stack这样的类,需要的是深拷贝,需要自己写

4.赋值运算符重载(主要是把一个对象赋值给另一个对象)

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 
型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。 
函数原型:返回值类型 operator操作符(参数列表)
注意:
不能通过连接其他符号来创建新的操作符:比如operator@ 
重载操作符必须有一个类类型或者枚举类型的操作数

用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不  能改变其含义 
作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
操作符有一个默认的形参this,限定为第一个形参
.* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

// 全局的operator==

class Date

{

public:

Date(int year = 1900, int month = 1, int day = 1)

{

_year = year;

_month = month;

_day = day;

}

//private:

int _year;

int _month;

int _day;

};

// 这里会发现运算符重载成全局的就需要成员变量是共有的,那么问题来了,封装性如何保证?

// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。

bool operator==(const Date& d1, const Date& d2)

{

return d1._year == d2._year;

&& d1._month == d2._month

&& d1._day == d2._day;

}

void Test ()

{

Date d1(2018, 9, 26);

Date d2(2018, 9, 27);

cout<<(d1 == d2)<<endl;

}

class Date

{

public:

Date(int year = 1900, int month = 1, int day = 1)

{

_year = year;

_month = month;

_day = day;

}

// bool operator==(Date* this, const Date& d2)

// 这里需要注意的是,左操作数是this指向的调用函数的对象

bool operator==(const Date& d2)

{

return _year == d2._year;

&& _month == d2._month

&& _day == d2._day;

}

private:

int _year;

int _month;

int _day;

};

void Test ()

{

Date d1(2018, 9, 26);

Date d2(2018, 9, 27);

cout<<(d1 == d2)<<endl;

}

让自定义类型可以像内置类型一样使用运算符需要哪个运算符,就重载哪个运算符

运算符重载跟函数重载,都有用了重载这个词,但是两个地方之间没有关联。

1.函数重载时支持定义同名函数

2.运算符重载时为了让自定义类型可以像内置类型一样取使用运算符

赋值是把值赋给变量,拷贝构造是创建一个对象时,拿同类对象初始化它。赋值拷贝时两个对象都已经存在

赋值重载:两个已经存在的对象拷贝

拷贝构造:拿一个已经存在的对象取构造初始化另一个要创建的对象

赋值运算符重载

class Date

{

public :

Date(int year = 1900, 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;

}

}

private:

int _year ;

int _month ;

int _day ;

};

赋值运算符主要有四点:

1.参数类型

2.返回值

3.检测是否自己给自己赋值

4.返回*this

5.一个类如果没有显示定义赋值运算符重载,编译器也会生成一个,完成对象俺字节序的值拷贝

class Date

{

public:

Date(int year = 1900, int month = 1, int day = 1)

{

_year = year;

_month = month;

_day = day;

}

private:

int _year;

int _month;

int _day;

};

int main()

{

Date d1;

Date d2(2018,10, 1);

// 这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的。

d1 = d2;

return 0;

}

那么编译器生成的默认赋值重载函数已经可以完成字节序的值拷贝了,我们还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。

class String

{

public:

String(const char* str = "")

{

_str = (char*)malloc(strlen(str) + 1);

strcpy(_str, str);

}

~String()

{

cout << "~String()" << endl;

free(_str);

}

private:

char* _str;

};

int main()

{

String s1("hello");

String s2("world");

s1 = s2;

}

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

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

相关文章

AutoSAR系列讲解(实践篇)10.3-BswM配置

目录 一、ECU State Handing(ESH) 二、Module Initialization 三、Communication Control 说起BswM的配置,其实博主问过很多朋友了,大家基本都只用自动配置;很少有用到手动配置的时候,对于刚刚入门的大家来说,掌握自动配置基 本也就足够了。 一、ECU State Handing(…

【雕爷学编程】MicroPython动手做(12)——掌控板之Hello World

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

内网隧道代理技术(十四)之 Earthworm的使用(一级代理)

Earthworm的使用(一级代理) ew 全称是EarchWorm,是一套轻量便携且功能强大的网络穿透工具,基于标准C开发,具有socks5代理、端口转发和端口映射三大功能,可在复杂网络环境下完成网络穿透,且支持全平台(Windows/Linux/Mac)。该工具能够以“正向”、“反向”、“多级级联”…

谷粒商城第七天-商品服务之分类管理下的删除、新增以及修改商品分类

目录 一、总述 1.1 前端思路 1.2 后端思路 二、前端部分 2.1 删除功能 2.2 新增功能 2.3 修改功能 三、后端部分 3.1 删除接口 3.2 新增接口 3.3 修改接口 四、总结 一、总述 1.1 前端思路 删除和新增以及修改的前端无非就是点击按钮&#xff0c;就向后端发送请求…

动脑学院Jetpack Compose学习笔记

最近b站学习了一下Compose相关内容&#xff0c;整理了相关笔记&#xff0c;仅供大家参考。 资源链接如下&#xff0c;象征性收取1个积分 https://download.csdn.net/download/juliantem/88125198

C数据结构——无向图(邻接表方式) 创建与基本使用

源码注释 // // Created by Lenovo on 2022-05-17-下午 4:37. // 作者&#xff1a;小象 // 版本&#xff1a;1.0 //#include <stdio.h> #include <malloc.h>#define TRUE 1 #define FALSE 0#define MAX_ALVNUMS 100 // 最大顶点数/** 定义链队*/ typedef int QEle…

服务器部署Go项目

最近在研究服务器部署项目&#xff0c;用了好几种办法成功部署。这些方法互有利弊&#xff0c;本文就逐一详细演示说明&#xff1a; 目录 1.服务器下载Go环境&#xff0c;直接将项目代码放到服务器上运行 2.服务器不下载Go环境&#xff0c;本地将项目打包成可执行的二进制…

【小程序】快来开发你的第一个微信小游戏(详细流程)

&#x1f973; 作者&#xff1a;伯子南 &#x1f60e; 坚信&#xff1a; 好记性不如乱笔头&#xff0c;独乐乐不如众乐乐 &#x1f4aa; 个人主页&#xff1a;https://blog.csdn.net/qq_34577234?spm1010.2135.3001.5421 &#x1f46c;&#x1f3fb; 觉得博主文章不错的话&…

java基础String类的特性

文章目录 1 String的特性2 String的内存结构1&#xff1a;拼接相关2&#xff1a;new相关3&#xff1a;intern() 1 String的特性 java.lang.String 类代表字符串。Java程序中所有的字符串文字&#xff08;例如"hello" &#xff09;都可以看作是实现此类的实例。 字符…

Python面向对象(二)(构造方法、其它内置方法)

构造方法 属性&#xff08;成员变量&#xff09;的赋值 class Student:name None # 姓名age None # 年龄tel None # 手机号stu1 Student() stu1.name "张三" stu1.age 20 stu1.tel "1212121211"stu2 Student() stu2.name "张…

Pytorch气温预测实战

数据集 数据有8个特征&#xff0c;一个标签值 自变量因变量yearactual:当天的真实最高温度monthdayweek:星期几temp_1:昨天的最高温度temp_2:前天的最高温度值average:在历史中&#xff0c;每年这一天的平均最高温度friend:朋友猜测的温度 year,month,day,week,temp_2,temp_…

【Linux】HTTPS协议是如何保证数据安全的

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;基础概念…

【Linux】进程篇Ⅰ:进程信息、进程状态、环境变量、进程地址空间

文章目录 一、概述二、查看进程信息1. 系统文件夹 /proc2. 用户级工具 ps3. getpid() 函数&#xff1a;查看进程 PID4. 用 kill 杀进程5. 进程优先级 二、进程状态分析0. 1. R (running) 运行状态2. S (sleeping) 休眠状态3. D (disk sleep) 不可中断的休眠状态4. T (stopped) …

【数据结构】顺序表(SeqList)(增、删、查、改)详解

一、顺序表的概念和结构 1、顺序表的概念&#xff1a; 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 2、顺序表的结构&#xff1a; &#xff08;1&#xff09;静态顺序表&#xff1a;使…

Redis Cluster 在Spring中遇到的问题

Redis集群配置可能会在运行时更改。可以添加新节点&#xff0c;可以更改特定插槽的主节点。还有可能因为master宕机或网络抖动等原因&#xff0c;引起了主从切换。 无法感知集群槽位变化 SpringBoot2.x 开始默认使用的 Redis 客户端由 Jedis 变成了 Lettuce&#xff0c;但是当…

忽略nan值,沿指定轴计算标准(偏)差numpy.nanstd()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 沿指定轴方向 计算标准(偏)差 numpy.nanstd() [太阳]选择题 import numpy as np a np.array([[1,2],[np.nan,3]]) print("【显示】a ") print(a) print("【执行】np.std(a)&qu…

QT项目代码去UI界面常用开发步骤

QT项目代码去UI界面常用开发步骤 因项目开发需求&#xff0c;领导要求整个QT项目中不要用UI方式来实现界面&#xff0c;这样能保障程序运行稳定性以及代码的逻辑和可读性,先记录具体操作步骤如下&#xff1a; 1、首先我们通过拖控件的方式来实现界面的设计效果&#xff0c…

ARM汇编基本变量的定义和使用

一、ARM汇编中基本变量是什么? 数字变量: GBLA LCLA SETA 逻辑变量:GBLL LCLL SETL 字符串:GBLS LCLS SETLS 注意需要TAB键定义变量和行首改变值 二、使用步骤 1.引入库 代码如下(示例): GBLA led_num Reset_Handler PROCEXPORT Reset_Handler [WEA…

HCIP BGP综合实验

题目 1、AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24该地址不能在任何协议中宣告&#xff1b; 2、AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以互相通讯&#xff1b; 3、AS间的骨干链路I…

Vue3搭建启动

Vue3搭建&启动 一、创建项目二、启动项目三、配置项目1、添加编辑器配置文件2、配置别名3、处理sass/scss4、处理tsx 四、添加Eslint 一、创建项目 npm create vite 1.project-name 输入项目名vue3-vite 2.select a framework 选择框架 3.select a variant 选择语言 二、启…