C++类中的六大默认成员函数详解
作者:wuqiongjin 发布时间:2022-09-18 01:33:01
在C++中,当你去创建一个类的时候,即便这个类是空类,也会自动生成下面6个默认成员函数,在本篇博客中,我将逐一分析下面6个默认成员函数。
构造函数
构造函数并不是去构造函数的函数,而是去对函数进行初始化的函数。构造函数的函数名与类名相同,当我们每次创建类对象的时候,就会自动调用构造函数。构造函数在对象的生命周期中只会调用1次。
class Date
{
public:
//构造函数
Date(int year = 2021, int month = 4, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
构造函数的几个特点:
①函数名与类名相同
②无返回值
③对象实例化时编译器自动调用对应的构造函数
④构造函数可以重载
class Date
{
public:
//构造函数的重载:
//无参的构造函数
Date()
{}
//需要传参的构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
⑤如果类中没有显式定义构造函数(就是自己没有去定义构造函数),那么编译器会自动生成一个无参的默认构造函数;
如果类中显式定义了构造函数,那么编译器将不再生成,而是去使用用户定义的构造函数。
⑥默认构造函数只能同时存在1个。默认构造函数分为以下3种:①无参的构造函数 ②全缺省的构造函数 ③编译器默认生成的构造函数
默认构造函数的共同特点是:不用传参就可以调用
class Date
{
public:
//下面2种和 当你不写构造函数时编译器自动生成的默认构造函数只能同时存在1种
//无参的
Date()
{
_year = 2021;
_month = 4;
_day = 11;
}
//全缺省的
Date(int year = 2021, int month = 4, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
⑦编译器生成的默认的构造函数,对内置类型(int, char, double...)不会做任何处理,但是会针对自定义类型的成员,调用它的构造函数去进行初始
构造函数调用的2种写法:
int main()
{
//无参时
Date d;
//单个参数
Date(1);
Date d1 = 2;//这种写法会发生隐式类型转换
//多个参数
Date(2021, 4, 11);
Date d2 = {2021, 4, 11};//C++11中才支持的写法
}
构造函数与初始化列表
初始化列表:以冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
初始化列表有什么用?
初始化列表,顾名思义就是对对象进行初始化的,但是我们已经可以在构造函数体内进行初始化了(通过对成员变量进行赋值来进行初始化),为什么还需要初始化列表?
这是因为,有些类型的数据无法通过在构造函数体内进行赋值来进行初始化。这样的数据类型有下面3种:
引用成员变量
const成员变量
自定义类型成员 (且它的类没有默认构造函数[即,它必须要进行传参])
上面的三种数据类型有一个共同的特点,它们都要求你在定义变量的时候进行赋值。
比如,引用成员变量,使用引用的时候必须进行初始化,否则语法就是错误的。
析构函数
析构函数的作用与构造函数相反,在对象的生命周期结束的时候会自动调用析构函数,完成类的一些资源清理的工作。
析构函数的特点:
析构函数名是在类名的前面加上~
无参,无返回值
一个类中有且只有1个析构函数。如果未显式定义,系统会自动生成默认的析构函数。(如果定义了,则采用显式定义的)
对象生命周期结束时,C++编译系统会自动调用析构函数
编译器生成的默认的析构函数,对内置类型(int, char, double...)不会做任何处理,但是会针对自定义类型的成员,会去调用它的析构函数
析构函数的一般使用情况:
一般使用在那些涉及到动态内存开辟空间的类中,因为这样的对象需要对其动态开辟的空间进行释放。
class Stack
{
public:
//构造函数
Stack(int n = 3)
{
_a = (int*)malloc(sizeof(int)*n);
_size = 0;
_capacity = n;
}
//析构函数
~Stack()
{
free(_a);
_size = _capacity = 0;
}
private:
int* _a;
int _size;
int _capacity;
};
拷贝构造函数
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象去创建新的对象时,编译器会自动调用拷贝构造函数。
拷贝构造函数的特点:
拷贝构造函数是构造函数的一个重载
拷贝构造函数的参数只有1个,且必须使用引用传参,如果使用引用传值的形式会引发无限递归。
拷贝构造函数的2种调用方法(完全等价的):
int main()
{
Date d1(1);
//拷贝构造函数
Date d2(d1); //1
Date d3 = d1; //2
return 0;
}
赋值运算符重载
在了解赋值运算符重载之前,我们需要先知道什么是运算符重载。
运算符重载
运算符重载是具有特殊函数名的函数。
函数名:关键字operator后面接需要重载的运算符符号(如:operator>)
函数原型:返回类型 operator操作符 (参数列表)
注意:
operator后面必须跟着的是操作符(这样是不可以的 operator@)
重载操作符必须有一个类类型或者枚举类型的操作数
用于内置类型的操作符,其含义无法改变。(比如内中的整型+,3+5这其中的+的意义不会改变)
this指针为限定的第一个形参,也就是this作为第一个操作数
.*、::、sizeof、?:、. 这5个操作符无法进行重载。
赋值运算符重载
class Date
{
public:
Date(int year = 2021, int month = 4, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
//赋值运算符重载
Date& operator=(const Date& d)
{
_year = d._day;
_month = d._month;
_day = d._day;
return *this;
}
private:
int _year;
int _month;
int _day;
};
注意:赋值运算符重载必须有返回值,如果没有返回值的话,无法解决 a = b = c 这种连续赋值的操作。
拷贝构造函数与赋值运算符重载
Date d1(1);
Date d2(0);
//赋值运算符重载
d2 = d1; //注意,只有2个操作数都是已经定义过的变量时,才会调用赋值运算符重载
//拷贝构造函数
Date d3(d1);
浅拷贝
浅拷贝是你在没有写拷贝构造函数和operator=时,编译器自动调用的默认成员函数。它的功能是将对象以字节的为单位拷贝过去。
取地址与const取地址操作符重载
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可(编译器默认的基本就够用了),只有特殊情况,才需要重载,比如想让别人获取到指定的内容。
class Date
{
public:
//取地址操作符重载
Date* operator&()
{
return this;
}
//const取地址操作符重载
const Date* operator&()const
{
return this;
}
private:
int _year;
int _month;
int _day;
};
class Date
{
public:
//初始化列表
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
来源:https://blog.csdn.net/weixin_51696091/article/details/115586185


猜你喜欢
- 详解Kotlin:forEach也能break和continue这样的问题。也就是说,他们想用forEach而不是for循环,因为这很fp,
- Pattern类定义 public final class Pattern
- 本文实例为大家分享了Unity实现弧形移动效果的具体代码,供大家参考,具体内容如下一、实现效果二、第一种实现方法——弧形插值using Un
- 在android6.0之后谷歌对指纹识别进行了官方支持,今天还在放假,所以就随意尝试了一下这个api,但是遇到了各种各样的问题 ①
- 喜欢另辟蹊径的我,在这里废话不多说了,直接上代码和图片了。效果图如下:第一步:MainActivity的代码如下:package net.l
- 假如使用绝对路径,没有任何问题,就是移植性不太好。假如使用相对路径,则要注意当前路径“.”是在哪儿?一般我们都会在配置文件中加入log文件的
- 使用场景:自己项目对接多个外部系统,各个外部系统使用的字段并没有统一,所以要根据不同系统动态的输出序列化数据,使适应各个系统的要求实现方式使
- C#实现的Check Password,并根据输错密码的次数分情况锁定账户:如果输入错误3次,登录账户锁定5分钟并提示X点X分后重试登录。如
- 效果图在开发APP中,经常要实现圆形头像,那么该如何实现呢?要裁剪吗,要重写draw函数吗?不用,只用一行代码就可以实现Glide实现圆形图
- 在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>
- GUID是一个128位长的数字,一般用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台
- 本文实例为大家分享了Android RecyclerView使用的具体代码,供大家参考,具体内容如下package com.itheima7
- 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下:public class Sample1 {  
- 纸上得来终觉浅,觉知此事要躬行。楔子本文适合:对Spring Security有一点了解或者跑过简单demo但是对整体运行流程不明白的同学,
- C#是一门强类型语言,一般情况下最好避免将一个类型转换成另一个类型,但是有些时候又不得不进行类型转换,那么就出现几种强转方式。1. 括号强转
- 本文实例讲述了C#实现AddRange为数组添加多个元素的方法。分享给大家供大家参考。具体实现方法如下:ArrayList ab = new
- ScrollView可实现控件在超出屏幕范围的情况下滚动显示。用法:在XML文件中将需滚动的控件包含在ScrollView中,当控件超出屏幕
- 背景在使用Spring Boot Mvc的项目中,使用Long类型作为id的类型,但是当前端使用Number类型接收Long类型数据时,由于
- 实例如下所示:/** * 创建多级目录文件 * * @param path 文件路径 * @throws IOException */pri
- ManualResetEvent表示线程同步事件,可以对所有进行等待的线程进行统一管理(收到信号时必须手动重置该事件)其构造函数为:publ