【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

这篇具有很好参考价值的文章主要介绍了【C++系列P4】‘类与对象‘-三部曲——[类](2/3)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

 前言

  • 大家好吖,欢迎来到 YY 滴 C++系列 ,热烈欢迎!
  • 【 '类与对象'-三部曲】的大纲主要内容如下

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

  • 如标题所示,本章是【 '类与对象'-三部曲】三章中的第二章节——类章节,主要内容如下:

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

目录

一.类

1.类的组成与计算类的大小(含结构体内存对齐规则)

二. 空类的大小

三.内部类

四.类的六个默认成员函数

1.构造函数

一.什么时候需要自己写构造函数? 

二.构造函数可以使用重载和不可以使用重载的情况

2.析构函数 

一.什么时候需要自己写析构函数? 

3.拷贝构造函数  

一.什么时候需要自己写拷贝构造函数?   

二.默认拷贝构造(浅拷贝)的缺陷:

4.运算符重载函数

一.运算符重载函数和构造函数使用区别:

5.赋值重载函数

6.取地址与取地址重载

五.初始化列表

一.初始化列表和构造函数的关系

二.初始化列表基本结构 

三.初始化列表使用场景

四.尽量使用初始化列表初始化

五.成员变量在初始化列表中的初始化顺序


一.类

  1. C++兼容C,C语言中的结构体strcut也算是一种类,是public(公有)的,可以被类外直接访问。
  2. 类中的函数默认是内联函数,具体是否是内联函数编译器会判断。如果将其定义和声名分开,即类放在.h文件,定义函数放在.cpp文件,函数不为内联函数

1.类的组成与计算类的大小(含结构体内存对齐规则)

  •  访问限定符划分类中既有成员变量,又有成员函数

【C++系列P4】‘类与对象‘-三部曲——[类](2/3) 【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

计算类的大小,只用考虑成员变量的大小

例如:上图中,类的大小为8字节

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

PS:内存对齐,本质上是牺牲空间换取效率。通过调整默认对齐数可以对这一过程进行动态调整。


二. 空类的大小

  • 没有成员变量的类对象,需要 1byte ,是为了占位,表示对象存在.

三.内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中 的所有成员。但是外部类不是内部类的友元。

 特性:

  • 内部类可以定义在外部类的public、protected、private都是可以的。
  • 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • sizeof(外部类)=外部类,和内部类没有任何关系。

四.类的六个默认成员函数

特点:

  • 当没有显式定义(我们不主动写时),编译器会自动生成

1.构造函数(第一个)

  •  默认构造函数(3种):(1) 类自己生成的函数(2)无参 (3)全缺省的函数
  •  特征:  (不传参就可以调用)  

 构造函数的主要任务是初始化对象,如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义,编译器将不再生成。

  • 运作上看,当对象实例化时,编译器会自动调用
  • 形态上看,其名字与类名相同且无返回值
  • 注意点,构造函数允许重载

一.什么时候需要自己写构造函数? 

需要自己写的情况:

  •  一般情况下,有内置类型成员,要自己写(否则会初始化成随机值

不需要自己写的情况:

  • 内置类型成员都有缺省值时,且初始化符合要求,可以考虑让编译器自己生成
  • 全部都是自定义类型成员(例如:Stack),可以考虑让编译器自己生成

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

                             注意!!!

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


二.构造函数可以使用重载和不可以使用重载的情况

  •   构造函数可以用重载的情况:
typedef int DataType;
class Stack
{
public:
	Stack(DataType* a, int n)    //特定初始化
	{
		cout << "Stack(DataType* a, int n)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * n);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		memcpy(_array, a, sizeof(DataType) * n);

		_capacity = n;
		_size = n;
	}
    //调用时可用以用d1,使用上方的构造函数
	Stack d1(int, 11); 
	//Stack d1(); // 不可以这样写,会跟函数声明有点冲突,编译器不好识别
	Stack d2;
    //调用时可以用d2,使用下方的构造函数

	Stack(int capacity = 4)    //构造函数(全缺省)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}

		_capacity = capacity;
		_size = 0;
	}
/*以下代码仅为完整性
void Push(DataType data)
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	void Pop()
	{
		if (Empty())
			return;
		_size--;
	}

	DataType Top() { return _array[_size - 1]; }
	int Empty() { return 0 == _size; }
	int Size() { return _size; }
~Stack()
	{
		cout << "~Stack()" << endl;
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}*/
private:
	DataType* _array;
	int _capacity;
	int _size;
};
  • 构造函数不能用重载的情况:无参调用存在歧义
// 构成函数重载
	// 但是无参调用存在歧义
	  Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

2.析构函数 (第二个)

析构函数的主要任务是清理对象;

  • 运作上看,当对象生命周期结束时,编译器会自动调用
  • 形态上看,其在类名前加上~且无返回值
  • 注意点,析构函数不允许重载

默认析构函数与默认构造函数类似,编译器对内置类型成员不做处理,对自定义类型会去调用它的析构函数。


一.什么时候需要自己写析构函数? 

需要自己写的情况:

  • 有动态申请资源时,需要自己写析构函数释放空间。(防止内存泄漏

不需要自己写的情况:

  • 没有动态申请资源时,不需要自己写,系统会自动回收空间。
  • 需要释放资源的对象都是自定义类型时,不需要自己写

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


3.拷贝构造函数  (第三个)

行为:

  • 在创建对象时,创建一个与已存在对象一模一样的新对象

拷贝构造函数:

  • 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)
  • 在用已存在的类类型对象创建新对象时由编译器自动调用(区分于构造函数)
  • 拷贝构造函数构造函数的一个重载形式
已知类Date,已经有实例化的对象 Date d1;
此时想得到一个和d1一模一样的对象d2;
Date d2(d1);
类中若有拷贝构造Date (const Date d);
直接进行调用;
d2传给没有显示的this指针,d1传给const Date d;
Date d2(const Date d1)
  • 拷贝构造函数的参数只有一个且必须是类类型对象引用
当拷贝构造函数为 Date(const Date &d);//引用
Date(const Date d);//错误写法

Date(const Date &d)
	{
		this->_year = d.year;
		this->_month =d.month;
		this->_day =d.day;
	}
//this 为d2的指针,d为拷贝的类d1
  • 原因:【使用传值方式编译器直接报错,因为会引发无穷递归调用】(错误方式) 

       【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


一.什么时候需要自己写拷贝构造函数?   

默认生成的拷贝构造函数为:浅拷贝

需要自己写的情况:

  • 自定义类型必须使用拷贝构造(深拷贝) 

不需要自己写的情况:

  • 内置类型直接拷贝(浅拷贝/值拷贝)

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

例:Date类中都是内置类型,默认生成的拷贝构造函数为浅拷贝可以直接用;

而Stack类为自定义类型,其中有a指针指向一块新开辟的空间。此时需要自己写拷贝构造函数。


二.默认拷贝构造(浅拷贝)的缺陷:

浅拷贝的缺陷(默认拷贝构造运用 引用 防止死递归的后遗症)

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


4.运算符重载函数(第四个)

运算符重载:

  • 参数类型:const T& (传递引用可以提高传参效率) 
  • 函数名:关键字operator后面接需要重载的运算符符号
  • 函数原型:返回值类型+operator操作符+(参数列表)

运算符重载 底层转化演示:

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

 注意:

  • 不能通过连接其他符号来创建新的操作符:例如operator@
  • 重载操作符必须有一个类型参数
  • 用于内置类型的运算符,其含义不能改变:例如+
  • 作为类成员函数重载时,其形参看起来比操作数少一个(因为成员函数的第一个参数为隐藏的this)
  • .* / :: /sizeof/ ?: /./这五个运算符不能重载 

一.运算符重载函数和构造函数使用区别:

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


5.赋值重载函数(第四个的分支)

赋值运算符重载格式:

  • 参数类型:const T& (传递引用可以提高传参效率) 
  • 返回值类型:T&  (返回引用可以提高返回的效率,有返回值的目的是为了支持连续赋值
  • 检测是否可以自己给自己赋值
  • 返回 *this:(对this指针解引用,要符合连续赋值的含义)

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)【C++系列P4】‘类与对象‘-三部曲——[类](2/3)

  • 赋值运算符只能重载成为类的成员函数而不能重载成全局函数(如果重载成全局函数,编译器会生成一个默认运算符重载)
  • 用户没有显示实现时,编译器会生成一个默认赋值运算符重载以值的方式(浅拷贝)逐字节拷贝。(注意点:内置类型成员变量直接赋值,只有自定义成员变量需要调用对应的赋值运算符重载

【C++系列P4】‘类与对象‘-三部曲——[类](2/3)


6.取地址与取地址重载(第五个&第六个)

引入: 内置类型取地址时有取地址操作符,而自定义类型呢?于是出现了取地址重载。它用到的场景非常少,可以说取地址重载——补充这个语言的完整性,更加系统。 

这两个默认成员函数一般不用重新定义 ,编译器默认会生成

  • 这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容! (设为nullptr)

代码演示:

class Date
{ 
public :
 Date* operator&()
 {
 return this ;
// return nullptr;让普通成员的this指针不被取到
 }
 
 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;
};

三.初始化列表使用场景

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时 )

缺省值与初始化列表的关系: (下列代码中 int x 有演示)

  • 初始化列表没显式定义,缺省值给到初始化列表
  • 初始化列表显式定义,以初始化列表为主

代码展示: 

class A
{
  public:                内置类型可以放到初始化列表中初始化
    A(int a)
    :_a(a)
    {}
  private:
    int _a;
};

class B
{
  public:
    B(int a, int ref)      必须放到初始化列表中进行初始化
    :_aobj(a)
    ,_ref(ref)
    ,_n(10)
    {}

  private:
    A _aobj; // 没有默认构造函数  (无参/全缺省/默认生成)
    int& _ref; // 引用
    const int _n; // const 

    int x = 3;    缺省值为3,缺省值是给初始化列表的
                但是如果初始化列表中显式定义,则以初始化列表为主
};

四.尽量使用初始化列表初始化

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

五.成员变量在初始化列表中的初始化顺序

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

图示:

 【C++系列P4】‘类与对象‘-三部曲——[类](2/3)文章来源地址https://www.toymoban.com/news/detail-473764.html

到了这里,关于【C++系列P4】‘类与对象‘-三部曲——[类](2/3)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Docker学习三部曲】—— 入门篇

    1️⃣ 什么是 docker ? Docker 是 一种运行应用程序的平台,它可以使应用程序在容器中不受环境差异的影响进行部署和运行 。Docker 的流行度越来越高,是因为它可以帮助在不同的开发者和开发团队之间实现代码的共享和协同开发,并且大大 简化了应用程序的部署,提高了可移

    2023年04月20日
    浏览(49)
  • JavaCV人脸识别三部曲之二:训练

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本文是《JavaCV人脸识别三部曲》的第二篇,前文《视频中的人脸保存为图片》咱们借助摄像头为两位群众演员生成大量人脸照片,如下图,群众演员A的照片保存在 E:temp20211218\\001 man ,B的照片保存

    2024年02月11日
    浏览(37)
  • Java版人脸跟踪三部曲之三:编码实战

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 作为《Java版人脸跟踪三部曲》系列的终篇,本文会与大家一起写出完整的人脸跟踪应用代码 前文《开发设计》中,已经对人脸跟踪的核心技术、应用主流程、异常处理等方方面面做了详细设计,建

    2024年02月12日
    浏览(37)
  • JavaCV人脸识别三部曲之三:识别和预览

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 《视频中的人脸保存为图片》 《训练》 《识别和预览》 作为《JavaCV人脸识别三部曲》的终篇,今天咱们要开发一个实用的功能:有人出现在摄像头中时,应用程序在预览窗口标注出此人的身份,效

    2024年02月11日
    浏览(39)
  • vscode上的git三部曲+git pull操作

    git三部曲:git add .、git commit -m \\\'\\\'、git push,命令在连接远程仓库的本地仓库路径下的终端执行。 vscode上的可视化操作如下:  1、对仓库里的文件做更改,让仓库操作的地方有变化。 2、 点击+号,让文件进入缓存,此步骤相当于终端执行命令git add .  3、在这里输入信息并点击

    2024年02月11日
    浏览(39)
  • Java版人脸跟踪三部曲之二:开发设计

    如何开发Java版人脸跟踪应用?本篇给出了设计大纲,并解释了相关的重要知识点 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇是《Java版人脸跟踪三部曲》系列的第二篇,前文体验了人脸跟踪的效果,想要编码实现这样的效果,咱们需要做

    2024年02月12日
    浏览(42)
  • Go语言基准测试(benchmark)三部曲之一:基础篇

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos Go的标准库内置的testing框架提供了基准测试(benchmark)功能,可以用来验证本地方法在串行或者并行执行时的基准表现,帮助开发者了解代码的真实性能情况,例如一个方法执行一次的平均耗时,还能

    2024年02月06日
    浏览(50)
  • 数据结构:堆的三部曲(二)top K问题

    top k问题解决的是获取前几个最值的问题。 我们知道 堆的功能主要是选数,选出最大值或者最小值 。那么我们每次获取堆顶元素后,再将剩余元素调整成堆,就可以选出次大的数,如果我们只想要前k个最大值或者最小值,就只需要获取堆顶元素k次,调整k次。比如王者荣耀

    2024年02月02日
    浏览(40)
  • 大模型 Dalle2 学习三部曲(二)clip学习

    clip论文比较长48页,但是clip模型本身又比较简单,效果又奇好,正所谓大道至简,我们来学习一下clip论文中的一些技巧,可以让我们快速加深对clip模型的理解,以及大模型对推荐带来革命性的变化。 首选我们来看看clip的结构,如图clip结构比较直观,训练的时候把文本描述

    2024年02月09日
    浏览(39)
  • Go语言基准测试(benchmark)三部曲之三:提高篇

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos -《Go语言基准测试(benchmark)三部曲》已近尾声,经历了《基础篇》和《内存篇》的实战演练,相信您已熟练掌握了基准测试的常规操作以及各种参数的用法,现在可以学习一些进阶版的技能了,在面

    2024年02月06日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包