详解C++中类的六大默认成员函数

目录
  • 一、类的默认成员函数
  • 二、构造函数Date(形参列表)
    • 1、构造函数的函数名和返回值
    • 2、构造函数的调用
    • 3、构造函数的重载
    • 4、系统生成的默认构造函数
    • 5、系统生成的默认构造函数的作用
    • 6、可以在内置类型的成员变量的声明中给缺省值
    • 7、初始化列表初始化
    • 8、单参数构造(C++98)、多参数构造(C++11)
  • 三、析构函数~Date()
    • 1、析构函数的函数名、参数和返回值
    • 2、析构函数的特点
    • 3、编译器生成的默认析构函数
  • 四、拷贝构造Date(const Date& d)
  • 五、赋值运算符重载Date& operator=(const Date& d )
  • 六、取地址操作符重载和const取地址操作符重载

一、类的默认成员函数

二、构造函数Date(形参列表)

构造函数主要完成初始化对象,相当于C语言阶段写的Init函数。

默认构造函数:无参的构造函数或全缺省的构造函数,二者只能存在一个,同时存在类中,调用时会出现二义性。

1、构造函数的函数名和返回值

构造函数的函数名和类名相同且无返回值

2、构造函数的调用

对象实例化时,编译器自动调用对应的构造函数且只调用一次

3、构造函数的重载

构造函数可以重载(多种初始化方式)注意:虽然全缺省和无参的构造函数构成重载,但是调用时存在二义性。

class Date
{
public:
	//构造函数的重载
	Date()
	{

	}
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;//创建并通过无参构造函数初始化对象时,无需加括号,不然变成了函数声明
	Date d2(2022,9,23);
	return 0;
}

4、系统生成的默认构造函数

如果我们在类中不写构造函数,C++编译器会在类中帮我们生成一个无参的构造函数。我们写了构造函数,那么系统将不会生成。

我们构造对象并调用构造函数时,初始化的数据是随机值:

5、系统生成的默认构造函数的作用

系统生成默认构造函数对内置类型不处理,对自定义类型调用他的构造函数。

注意:下方代码中,Date类中的内置类型将会调用Date类中的构造函数;Date类中的Time _t将会调用Time类中的构造函数。

6、可以在内置类型的成员变量的声明中给缺省值

C++11中针对内置类型不处理初始化为随机值的问题,打了补丁:内置类型成员变量在类中声明可以给默认值,甚至可以给动态开辟的缺省值,缺点是不能判断空间是否开辟成功。

注意这里的默认值是缺省值,不是初始化。初始化是要等对象调用时才叫初始化。

class Date
{
private:
	int _year=1;
	int _month=2;
	int _day=3;
	int* arr = (int*)malloc(sizeof(int) * 5);
};

这个特性只能用于解决默认构造函数初始化为随机值的问题。这个特性不能解决对象的多种初始化方式(这也是构造函数支持重载的原因),构造函数该写还是得自己写。

7、初始化列表初始化

  • 1、对象的每个成员变量是在初始化列表部分进行初始化,而函数体内的行为是对成员函数赋初值。
  • 2、如果没有在初始化列表里显示初始化某个成员函数,对于内置类型,有缺省值用缺省值,无缺省值初始化为随机值;对于自定义类型将会调用它的默认构造函数,没有找到默认构造就会报错。
  • 3、引用、const、无默认构造函数的自定义类型必须通过初始化列表初始化。
  • 4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
Date(int year=1,int month=1,int day=1)//缺省值
    :_year(year)//成员变量的定义
    ,_month(month)
    ,_day(day)
{}//成员变量的赋初值

总结:尽量使用使用初始化列表进行初始化,在类中尽量提供默认构造函数(最好是全缺省的默认构造函数)

8、单参数构造(C++98)、多参数构造(C++11)

class Date
{
public:
	Date(int year=1,int month=1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//单参数的构造,构造+拷贝,编译器直接优化为构造C++98
	Date d1 = 2022;
	//临时对象具有常性,构造+拷贝,编译器不会优化
	const Date& d2 = 2023;
	//多参数的构造C++11
	Date d3 = { 2022,10,16 };
	return 0;
}
  • 1、在构造时,支持等号加参数的构造形式,实际上发生的是隐式类型转换。2022由int类型隐式类型转换为Date型的临时对象,该临时对象再将它的值拷贝构造给d1。
  • 2、2023会隐式类型转换为Date类型的临时对象,具有常属性。d2是这个临时对象的引用,所以需要加上const。这里发生构造+拷贝构造,涉及临时对象的引用,所以编译器并不会发生优化。
  • 3、可以使用explicit关键字修饰构造函数,会禁止隐式类型转换。

三、析构函数~Date()

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。资源包括动态开辟的空间,文件的关闭等。相当于C语言阶段写的destroy函数。

1、析构函数的函数名、参数和返回值

析构函数的函数名是类名前加~,无参无返回值类型。

2、析构函数的特点

一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载

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

3、编译器生成的默认析构函数

对于编译器生成的默认析构函数,对自定义类型调用他的析构函数。对于内置类型,没有需要处理资源。

注意:Date类中的内置类型会调用Date类中的析构函数;Date类中的自定义类型Time _t会调用Time的析构函数。

四、拷贝构造Date(const Date& d)

1、拷贝构造函数是构造函数的重载

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

拷贝构造函数的函数名和构造函数相同无返回值,在参数上和构造函数构成重载。

2、拷贝构造函数的参数

拷贝构造函数的参数只有一个并且是类类型对象引用。如果使用传值传参的方式进行拷贝构造,在传值的过程中实参需要拷贝一份数据给形参,这个过程需要调用拷贝构造。形成层层传值引发对象的拷贝的递归(有递无归)调用。

3、若未显式定义,编译器会生成默认的拷贝构造函数

默认的构造函数对于内置类型按照字节拷贝。对于自定义类型则调用它的拷贝构造函数。

4、拷贝构造函数的深浅拷贝

通过默认的拷贝构造函数构造的对象,按字节完成拷贝。这种拷贝被称为浅拷贝(值拷贝)。

int main()
{
	Date d1(2022,9,24);
	Date d2(d1);
    return 0;
}

对于内置类型,使用浅拷贝即可,系统默认生成的就可以做到,所以我们不用动手写拷贝构造函数。注意这里有d1,d2两个对象,当main函数生命周期结束时,这两个对象均会发生一次析构,d2先析构,d1后析构。(后定义的先销毁,类似栈的后进先出原则)

但是浅拷贝对于占用“资源”的成员变量时(例如成员变量中有动态开辟或fopen的资源),指针虽然复制了,但是所指向的内容却没有复制,析构时存在同一块空间被释放两次的问题。需要进行深拷贝。深拷贝的拷贝构造函数必须自己手动实现。

class Stack
{
public:
	Stack(int capacity=100)//构造函数
	{
		_capacity = capacity;
		_top = 0;
		_arr = (int*)malloc(sizeof(int) * 5);
		if (_arr == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
	}
	//Stack(const Stack& st)//浅拷贝,栈这个类不能用浅拷贝
	//{
	//	_capacity = st._capacity;
	//	_top = st._top;
	//	_arr = st._arr;
	//}
	Stack(const Stack& st)//深拷贝
	{
		_capacity = st._capacity;
		_top = st._top;
		_arr = (int*)malloc(sizeof(int) * st._top);
		if (_arr == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_arr, st._arr,sizeof(int)*st._top);
	}
	~Stack()//析构函数
	{
		_capacity = 0;
		_top = 0;
		free(_arr);
		_arr = nullptr;
	}
private:
	int* _arr;
	int _top;
	int _capacity;
};

栈这个类因为成员变量中有动态开辟的空间,所以要用深拷贝。

5、拷贝构造函数调用场景

  • 1、使用已存在的对象创建新对象
  • 2、函数参数类型为类的类型对象(传值调用,实参拷贝给形参)
  • 3、函数返回值类型为类的良心对象(传值返回)

五、赋值运算符重载Date& operator=(const Date& d )

1、赋值运算符重载

只有赋值运算符是默认成员函数。

2、赋值运算符重载的注意事项

  • 1、参数是const T&,传引用可以减少一次拷贝构造。
  • 2、返回值是*this的引用,引用返回,减少一次拷贝构造,有返回值是为了支持函数的链式访问。
  • 3、务必检查下是否支持自己给自己赋值。
  • 4、赋值运算符重载必须是类中的默认成员函数,不能写在全局。
  • 5、系统默认生成的赋值运算符重载会完成值拷贝。

六、取地址操作符重载和const取地址操作符重载

Date* operator&()
{
    return this;
    //return nullptr;
}
const Date* operator&()const
{
    return this;
    //return nullptr;
}

不用自己写,除非想让别人通过取地址操作符获取到特定值(自己在重载函数内部写)或屏蔽类地址。

以上就是详解C++中类的六大默认成员函数的详细内容,更多关于C++类成员函数的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++中类的默认成员函数详解

    目录 一.构造函数 二.析构函数 三.拷贝构造函数 四.赋值函数(赋值运算符重载) 总结 C++中,对于任意一个类,都会为我们提供4个默认的成员函数(如果我们不显示的去声明)——构造函数.析构函数.拷贝构造函数.赋值函数.这些函数在特定的情况下会被自动调用,但自动调用并不意味着它们能像用户所期望的那样能实现特定的功能或者完成特定的任务,更多的时候需要我们自己实现这些函数的功能 A(); //默认的构造函数 ~A(); //析构函数 A(const A&); //默认的拷贝函数 A& ope

  • C++类中的六大默认成员函数详解

    在C++中,当你去创建一个类的时候,即便这个类是空类,也会自动生成下面6个默认成员函数,在本篇博客中,我将逐一分析下面6个默认成员函数. 构造函数 构造函数并不是去构造函数的函数,而是去对函数进行初始化的函数.构造函数的函数名与类名相同,当我们每次创建类对象的时候,就会自动调用构造函数.构造函数在对象的生命周期中只会调用1次. class Date { public: //构造函数 Date(int year = 2021, int month = 4, int day = 11) { _yea

  • C++类中六个默认的成员函数详解

    浅谈 先来说一下"this指针": C++中通过引入this指针解决该问题,暨:C++编译器给每个"非静态的成员函数"增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问,只不过所有的操作对用户是透明的,暨用户不需要来传递,编译器自动完成. 说了这么多其实编译器在生成程序时获取对象首地址的信息.然后将获取的对象的首地址存放在了寄存器中,成员函数的其它参数都是存放在栈中.而this指针参数则是

  • 详解C++中类的六大默认成员函数

    目录 一.类的默认成员函数 二.构造函数Date(形参列表) 1.构造函数的函数名和返回值 2.构造函数的调用 3.构造函数的重载 4.系统生成的默认构造函数 5.系统生成的默认构造函数的作用 6.可以在内置类型的成员变量的声明中给缺省值 7.初始化列表初始化 8.单参数构造(C++98).多参数构造(C++11) 三.析构函数~Date() 1.析构函数的函数名.参数和返回值 2.析构函数的特点 3.编译器生成的默认析构函数 四.拷贝构造Date(const Date& d) 五.赋值运算符重

  • C++中的四个默认成员函数与运算符重载详解

    本文主要给大家介绍了关于C++默认成员函数与运算符重载的相关内容,分享出来公的敬爱啊参考学习,话不多说,来一起看看详细的介绍: 一:类和对象的基础知识:类的定义,访问限定符,面向对象封装性,对象的大小计算等等.(编译环境为VS2015) 面向对象程序设计: 概念:(Object Oriented Programming,缩写:OOP)是一种程序设计范型,同时也是一种程序开发的方法.对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性.灵活性和扩展性. 类:类的基

  • 详解java设计模式之六大原则

    一.单一职责原则 1.单一职责定义 单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因. 单一职责原则告诉我们:一个类不能太"累"!在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责 变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封

  • 详解idea中web.xml默认版本问题解决

    问题描述:用idea的maven新建一个webapp项目,自动生成的web.xml默认版本是2.3版本(这版本连EL表达式都默认不能使用,无语了). Servlet 2.3: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "h

  • 详解如何修改nginx的默认端口

    首先找到配置文件在哪 whereis nginx.conf 你可以这几个目录都看一遍,看看配置文件在哪里.我这边正确目录是 /etc/nginx 其次找到真正的配置文件 cd 到 /etc/nginx 目录下 打开一看,懵了,哪里有 listen 80 这一行呢? 不过别着急,listen 80 肯定是有的,只不过没在这里面放了.你看,不是有 include 文件吗?在那里面也说不定. 好,然后去看看 sites-enabled 文件夹下有什么 好吧,这个文件又指向了 site-availabl

  • C语言详解如何应用模拟字符串和内存函数

    目录 1.strlen 求字符串长度 使用案例: 1.计数法 2.不创建临时变量计数器-递归 3.指针-指针的方式 2.长度不受限制的字符串函数 1.strcpy 使用案例: 模拟实现: 2.strcat 使用案例: 模拟实现: 3.strcmp-比较字符串首字母的大小 使用案例: 模拟实现: 3.长度受限制的字符串函数  1.strncpy 使用案例: 2.strncat  使用案例: 3.strncmp 使用案例: 4.strstr-找子串  使用案例: 模拟实现: 5.strtok 用法:

  • 详解C++调用Python脚本中的函数的实例代码

    1.环境配置 安装完python后,把python的include和lib拷贝到自己的工程目录下 然后在工程中包括进去 2.例子 先写一个python的测试脚本,如下 这个脚本里面定义了两个函数Hello()和_add().我的脚本的文件名叫mytest.py C++代码: #include "stdafx.h" #include <stdlib.h> #include <iostream> #include "include\Python.h&quo

  • 详解python中的lambda与sorted函数

    lambda表达式 python中形如: lambda parameters: expression 称为lambda表达式,用于创建匿名函数,该表达式会产生一个函数对象. 该对象的行为类似于用以下方式定义的函数: def <lambda>(parameters): return expression python中的lambda函数可以接受任意数量的参数,但只能有一个表达式.也就是说,lambda表达式适用于表示内部仅包含1行表达式的函数.那么lambda表达式的优势就很明显了: 使用lam

随机推荐