深蓝学院C++基础与深度解析笔记 第 11 章 类

news2024/9/24 7:24:51

深蓝学院C++基础与深度解析笔记 第 11 章 类

1. 结构体与对象聚合
**● 结构体:**对基本数据结构进行扩展,将多个对象放置在一起视为一个整体

– 结构体的声明与定义(注意定义后面要跟分号来表示结束)
– 仅有声明的结构体是不完全类型( incomplete type )
– 结构体(以及类)的一处定义原则:翻译单元级别

● 数据成员(数据域)的声明与初始化

– ( C++11 )数据成员可以使用 decltype 来声明其类型,但不能使用 auto(除静态)
– 数据成员声明时可以引入 const 、引用等限定
– 数据成员会在构造类对象时定义
– ( C++11 )类内成员初始化
– 聚合初始化:从初始化列表到指派初始化器
#include <iostream>
struct Str 
{
     int x;
     int y;
}

int main()
{
  Str m_str;
  m str.x = 3;
  std::cout < m str.x < std::endl; 
}

有声明即可定义指针,结构体内可以使用decltype(),不可以使用auto;
● mutable 限定符,只能在结构体内限定定义
● 静态数据成员 多个对象之间共享的数据成员

– 定义方式的衍化

在这里插入图片描述
● C++98 :类外定义, const 静态成员的类内初始化
● C++17 :内联静态成员的初始化

– 可以使用 auto 推导类型

● 静态数据成员的访问

– “.” 与“ ->” 操作符
– “::” 操作符

● 在类的内部声明相同类型的静态数据成员

2. 成员函数(方法)

C语言不可以在结构体中定义函数;C++可以在结构体中定义函数,作为其成员的一部分:对内操作数据成员,对外提供调用接口:

– 在结构体中将数据与相关的成员函数组合在一起将形成类,是 C++ 在 C 基础上引入的概念
– 关键字 class, struct在此类似于public:
– 类可视为一种抽象数据类型,通过相应的接口(成员函数)进行交互
– 类本身形成域,称为类域 

● 成员函数的声明与定义:

– 类内定义(隐式内联)
– 类内声明 + 类外定义, 类外显示内联:inline
– 类与编译期的两遍处理:但是只能处理内置类型后置,自定义类型还是需要桉顺序使用
– 成员函数与尾随返回类型( trail returning type )

● 成员函数与 this 指针: str *const this

– 使用 this 指针引用当前对象
– 基于 const 的成员函数重载,this本身不能修改,但是this->x可以

**● 成员函数的名称查找与隐藏关系:**先找最小范围的作用域,在递归的寻找外一层的作用域

– 函数内部(包括形参名称)隐藏函数外部
– 类内部名称隐藏类外部
– 使用 this 或域操作符引入依赖型名称查找

ps: 类名::变量/函数即为该类下的变量/函数, :: 变量/函数:中省略::前的作用域范围即为全局的变量/作用域
● 静态成员函数:被所有对象共享

– 在静态成员函数中返回静态数据成员

● 成员函数基于引用限定符的重载( C++11 )

3. 访问限定符与友元
访问限定符与友元
● 使用 public/private/protected 限定类成员的访问权限

– 限定目标 : 访问权限的引入使得可以对抽象数据类型进行封装
– 类与结构体缺省访问权限的区别: C++默认:private, C 默认类似于public

● 使用友元打破访问权限限制 关键字 —— friend

– 声明某个类或某个函数是当前类的友元 慎用! —— 改变了【封装】  特性
– 在类内首次声明友元类或友元函数:可以先声明freined ,再定义;声明friend和声明函数是两个概念
  ● 注意使用限定名称引入友元并非友元类(友元函数)的声明: 假如使用了全局限定::则需要提前声明或定义函数
#include <iostream>
#include <vector>

class Str2;
class str 
{
    friend Str2;  // str2可以访问str的私有和保护类型的数据和函数 ,单向的   
public:
    inline static int x; 
private:
    int y;
};

class Str2 
{
    void fun() 
    {
        std::cout<<Str::x<< std::endl; 
    }
}; 
– 友元函数的类内外定义与类内定义

隐藏友元( hidden friend ):常规名称查找无法找到,在类内定义

● 好处:减轻编译器负担,防止误用
● 改变隐藏友元的缺省行为:在类外声明或定义函数

4. 构造、析构与复制成员函数

● 构造函数: 构造对象时调用的函数,无返回

– 名称与类名相同,无返回值,可以包含多个版本(可重载)
– 代理构造函数( C++11 ):使用别的函数完成构造,然后再执行本构造函数的内容

代理构造:
在这里插入图片描述
● 初始化列表: 区分数据成员的初始化与赋值,尽量和初始化顺序一致
在这里插入图片描述

– 通常情况下可以提升系统性能
– 一些情况下必须使用初始化列表(如类中包含引用成员)
– 注意元素的初始化顺序与其声明顺序相关,与初始化列表中的顺序无关(初始化列表可能顺序不一致,但是声明顺序是固定的,为了满足先构造的后销毁的原则)
– 使用初始化列表覆盖类内成员初始化的行为

先构造的后销毁,后构造的对象先销毁!
**● 缺省构造函数:**不需要提供实际参数就可以调用的构造函数

– 如果类中没有提供任何构造函数,那么在条件允许的情况下,编译器会自动合成一个缺省构造函数
– 合成的缺省构造函数会使用缺省初始化来初始化其数据成员
– 调用缺省构造函数时避免 most vexing parse
classname m();      //会把m认为是函数而不是对象

使用 = default 关键字定义缺省构造函数,只有编译器能合成的函数它才会去合成。

● 单一参数构造函数

– 可以视为一种类型转换函数
– 可以使用 explicit 关键字避免求值过程中的隐式转换

● 拷贝构造函数: 接收一个当前类对象的构造函数

– 会在涉及到拷贝初始化的场景被调用,比如:参数传递。因此要注意拷贝构造函数的形参类型
– 如果未显式提供,那么编译器会自动合成一个,合成的版本会依次对每个数据成员调用拷贝构造

● 移动构造函数 (C++11) :
将旧的对象转移给新对象后,旧的对象自动销毁。接收一个当前类右值引用对象的构造函数,进一步提升系统性能。

– 可以从输入对象中“  偷窃”  资源,只要确保传入对象处于合法状态即可
– 当某些特殊成员函数(如拷贝构造)未定义时,编译器可以合成一个,有移动调移动,没有移动调拷贝 (C++17)
– 通常声明为不可抛出异常的函数: noexcept,一旦抛出就会崩溃
– 注意右值引用对象用做表达式时是左值! 

PS:&& 代表右值引用

语义移动:
在C++中,移动语义是一种优化技术,用于在对象之间转移资源的所有权,而不是进行复制。 当你移动一个对象时,源对象的资源所有权转移到目标对象,源对象不再拥有该资源。

移动操作通常使用移动构造函数和移动赋值运算符来实现。这些特殊的成员函数允许你有效地将资源(如堆上的内存、文件句柄等)从一个对象转移到另一个对象,而无需进行代价昂贵的复制操作。

在进行移动操作时,原始对象的内存空间仍然存在,但它的状态可能会变为有效但不确定的状态。 是因为移动操作不对原始对象进行显式的清理或重置,这意味着你不能再对原始对象进行任何有意义的操作,因为它的资源已被移动到其他对象。对于移动后的原始对象,你可以选择销毁它或重新赋值为其他有效值。(为了避免误用移动操作导致不确定状态,建议清理!)

移动操作的主要好处是避免了不必要的复制开销,特别是在涉及大型对象或资源密集型对象时。通过移动对象,可以更高效地管理资源并提高程序的性能。

需要注意的是,只有具有可移动语义的对象才能被移动。这包括具有移动构造函数和移动赋值运算符的类,或者具有可移动成员的类(如std::unique_ptr、std::vector等)。

总结起来,移动操作将资源的所有权从一个对象转移到另一个对象,原始对象进入有效但不确定的状态,而移动后的对象获得资源的所有权。移动操作的主要目的是避免不必要的复制并提高程序性能。

● 拷贝赋值与移动赋值函数( operator = )

Str m;        //缺省构造函数  无参
Str m2 = m;   //拷贝赋值
Str m3(m2);   //移动构造函数
m3 = m;       //赋值运算符,会被翻译成 m3.opreator = (m);

拷贝赋值返回值是:类名&

– 注意赋值函数不能使用初始化列表
– 通常来说返回当前类型的引用
– 注意处理给自身赋值的情况
– 在一些情况下编译器会自动合成

● 析构函数

– 函数名:“ ” ~ 加当前类型,无参数,无返回值
– 用于销毁对象以及扫尾工作,执行完最后一句才会跳出执行内存回收。
– 注意内存回收是在调用完析构函数时才进行
– 除非显式声明,否则编译器会自动合成一个,其内部逻辑为平凡的
– 析构函数通常不能抛出异常

● 通常来说,一个类:

– 如果需要定义析构函数,那么也需要定义拷贝构造与拷贝赋值函数(显式地)
– 如果需要定义拷贝构造函数,那么也需要定义拷贝赋值函数
– 如果需要定义拷贝构造(赋值)函数,那么也要考虑定义移动构造(赋值)函数

没有定义在使用时就会造成二次释放或者多次释放
ps:

1. Rule of Three (三法则):
如果需要定义析构函数、拷贝构造函数或拷贝赋值函数中的任何一个,就需要显式定义这三个函数。

这个原则确保了资源管理的正确性和一致性。

2. Rule of Five (五法则):
在C++11及更高版本中,如果需要定义拷贝构造函数或拷贝赋值函数,就需要同时定义析构函数、拷贝构造函数和拷贝赋值函数,并且还要考虑定义移动构造函数和移动赋值函数。

这个原则兼容了移动语义,提高了对象的性能和效率。

请注意,这些名称是广为接受的通用术语,用于描述C++编程中的最佳实践。
● 示例:包含指针的类:

● default 关键字
– 只对特殊成员函数有效

● delete 关键字
=delete 表示禁用该函数

– 对所有函数都有效
– 注意其与未声明的区别
– 注意不要为移动构造(移动赋值)函数引入 delete 限定符

在这里插入图片描述

● 如果只需要拷贝行为,那么引入拷贝构造即可
● 如果不需要拷贝行为,那么将拷贝构造声明为 delete 函数即可
● 注意 delete 移动构造(移动赋值)对 C++17 的新影响

如果一个类有用户自定义的拷贝赋值运算符或拷贝构造函数,或者有用户自定义的析构函数,那么该类的相关默认函数的隐式定义会被弃用,并且在未来的C++版本中可能会被删除,需要显式定义相关函数来替代。

5. 字面值类,成员指针与 bind 交互

● 字面值类:可以构造编译期常量的类型

– 其数据成员需要是字面值类型
– 提供 constexpr / consteval 构造函数 (小心使用 consteval )
– 平凡的析构函数(没有自定义行为的析构函数)
– 提供 constexpr / consteval 成员函数 (小心使用 consteval )
– 注意:从 C++14constexpr / consteval 成员函数非 const 成员函数

constexprconsteval区别:

constexprconsteval是C++中的两个关键字,用于指定编译时常量表达式和编译时求值函数。

  1. constexpr

    • constexpr是C++11引入的关键字,用于声明一个编译时常量表达式。它可以应用于变量、函数、构造函数等。
    • constexpr声明的变量必须在编译时求值并产生常量结果,可以用于编译时常量的计算和编译时优化。
    • constexpr函数是在编译时进行求值的函数,它的参数和返回值必须是可编译时求值的表达式,函数体内只能包含可编译时求值的语句。
    • 在C++14之前,constexpr函数只能包含一些简单的计算和控制流程,而在C++14及以后的版本中,constexpr函数可以包含更复杂的逻辑。
  2. consteval

    • constevalC++20引入的关键字,用于声明一个编译时求值函数,要求在编译时执行。
    • consteval函数是在编译时强制执行的函数,它的参数和返回值必须是可编译时求值的表达式,函数体内只能包含可编译时求值的语句。
    • constexpr函数不同,consteval函数在编译时强制要求函数的调用结果能在编译期间确定,否则会引发编译错误。
    • consteval函数适用于需要在编译时执行的严格要求,可以用于生成更高效的代码,但使用场景相对较少。

总结:
constexpr用于声明编译时常量表达式和函数,而consteval用于声明在编译时强制求值的函数。它们都提供了在编译期间进行求值和优化的能力,但consteval更加严格,要求函数在编译时一定能求值,否则会导致编译错误。

● 成员指针

– 数据成员指针类型示例: int A::*;  即使A中没有任何东西
– 成员函数指针类型示例: int (A::*)(double);
– 成员指针对象赋值: auto ptr = &A::x;
   ● 注意域操作符子表达式不能加小括号(否则 A::x 一定要有意义,例如静态)
   – 成员指针的使用:
        ● 对象 .* 成员指针
        ● 对象指针 ->* 成员指针

无法加减,会被认为不是同一类型的,下式报错:
在这里插入图片描述

● bind 交互: 需要引入类的对象

– 使用 bind + 成员指针构造可调用对象
– 注意这种方法也可以基于数据成员指针构造可调用对象

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

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

相关文章

【Linux后端服务器开发】共享内存

目录 一、共享内存概述 二、共享内存&#xff08;IPC资源&#xff09;的查看——ipcm 三、共享内存的创建——shmget 四、共享内存的控制&#xff08;删除&#xff09;——shmctl 五、共享内存的关联——shmat 六、共享内存去关联——shmdt 七、进程间通信 一、共享内存…

力扣 -- 309. 最佳买卖股票时机含冷冻期

题目链接&#xff1a;309. 最佳买卖股票时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 参考代码&#xff1a; class Solution { public:int maxProfit(vec…

基于matlab使用深度神经网络对肿瘤图像进行分类(附源码)

一、前言 此示例演示如何使用 Inception-v3 深度神经网络对可能不适合内存的多分辨率全玻片图像 &#xff08;WSI&#xff09; 进行分类。 用于肿瘤分类的深度学习方法依赖于数字病理学&#xff0c;其中整个组织切片被成像和数字化。生成的 WSI 具有高分辨率&#xff0c;大约…

Openlayers实战:平移、弹性平移、飞行动画

Openlayers地图上经常会遇到这样的一种 场景,获取到某数据后,会重新定位中心点到某个位置,这里可以用setCenter([lon,lat]), 更可以用动画的形式展现。 在本实战中,展示出平移、弹性平移、飞行的动画。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是大剑…

【图像处理OpenCV(C++版)】——5.5 图像平滑之双边滤波

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

第三十六天 Java基础学习(三十)

一、Spring MVC 组件是将处理某类问题的代码进行封装的整体模块。一个大的问题可以拆分为不同的小问题&#xff0c;解决每个小问题的代码封装可以称之为组件&#xff0c;但是组件又是无法独立运行的&#xff0c;必须结合其他组件一起才能最终解决问题。就好比汽车&#xff0c;…

同步任务和异步任务的执行过程

同步任务和异步任务的执行过程 1、执行过程描述2、EventLoop的概念 1、执行过程描述 同步任务 是由JS主线程按次序执行异步任务委托给宿主环境执行已完成的异步任务对应的回调函数&#xff0c;会被加入到任务队列中等待执行JS的主线程的执行栈被清空后&#xff0c;会读取任务队…

Java026——System 类和Scanner 类

一、System 类 1.1、System 类提供的常用方法 方法 功能描述 ----------------------------------------------------------------------------------------------------------------------- currentTimeMillis() 返回当前计算机时间 和 格林威治时间&#xff…

mysql查询练习

1.创建表 CREATE TABLE worker( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) NOT NULL DEFAULT 群众 , 姓名 varchar(20) NOT NULL,出生日期 date NOT NULL,PRIMARY KEY (职工号))ENGINE…

4.3Java EE——一对多查询

用户与订单关联关系图​​​​​​​ 与一对一的关联关系相比&#xff0c;接触更多的关联关系是一对多&#xff08;或多对一&#xff09;。例如一个用户可以有多个订单&#xff0c;多个订单也可以归一个用户所有。用户和订单的关联关系如图。 一、<collection>元素 在MyB…

前端技术学习第九讲:VUE基础语法---VUE常用指令

VUE常用指令 在VUE学习中&#xff0c;通常使用相关指令使VUE对象中的内容与网页进行挂载绑定&#xff0c;是我们的数据与视图之间产生关联&#xff0c;完成渐进式动态效果。VUE指令都会以“v-”开头。 指令名描述v-text将文本内容挂载到页面元素中v-html将html代码展示到页面…

更开放、更高性能、更具规模,闪马智能布局AGI时代

7月6日&#xff0c;2023世界人工智能大会&#xff08;WAIC 2023&#xff09;在上海盛大开幕。本届大会以“智联世界 生成未来”为主题&#xff0c;聚焦通用人工智能发展&#xff0c;共话产业新未来。 8日上午&#xff0c;由上海闪马智能科技有限公司&#xff08;下称“闪马智能…

ZGC垃圾收集器(-XX:+UseZGC)

ZGC在jdk11只支持Linux版本&#xff0c;4TB的内存&#xff0c;STW时间控制在10ms内&#xff1b;jdk16已经支持16TB的内存&#xff0c;STW时间不超过1ms&#xff0c;下面主要针对jdk11版本的详解 一、堆内存结构 ZGC堆内存分为三种类型的页面即小页面&#xff08;空间大小2M&a…

CYCLO (L-ALA-L-ALA),5845-61-4,环(丙氨酰-丙氨酰),氨基酸中间体

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ 【产品描述】 cyclo(Ala-Ala)氨基酸中间体&#xff0c;主要由丙氨酰组成 【中文名称】环(丙氨酰-丙氨酰) 【英文名称】 cyclo(Ala-Ala)&#xff0c;CYCLO (L-ALA-L-ALA) 【结 构 式】 【CAS】5845-61-4 【分子式】C6H10N2O…

C#制作打包安装程序,安装程序类使用

这里写目录标题 安装Microsoft Visual Studio Installer Projects创建安装项目设置安装程序文件设置程序桌面图标给程序设置安装程序名称安装程序类怎么使用Installer1.cs自定义安装步骤自定义设置安装程序路径&#xff0c;让用户安装时不能选择安装路径生成安装包 安装Microso…

实例011 在状态栏中显示检查框

实例说明 在设计程序界面时&#xff0c;为了规范界面&#xff0c;可以将一些控件放置在状态栏中&#xff0c;这样既能起到控制程序的作用&#xff0c;又能使界面和谐、美观。运行程序&#xff0c;在窗体的状态栏中加入了显示时间检查框。效果如图1.11所示。 技术要点 在状态…

对Linux系统对Spark开发环境配置

单机版本 上传对应文件&#xff0c;解压文件&#xff0c;并查看 unzip scala-2.12.12.zip tar -xzf spark-3.2.0-bin-hadoop2.7.tgz 移动scala及spark安装包到指定目录下 在opt目录下移动该文件到/usr目录下 mv scala-2.12.12 /usr/scala/ mv spark-3.2.0-bin-hadoop2.7 /…

Django_使用redis缓存数据

目录 一、配置redis 二、缓存Django的默认session 三、使用django的缓存机制缓存数据 四、自定义缓存数据 源码等资料获取方法 一、配置redis 在settings中添加配置参数 # Django的缓存配置 CACHES {"default": {"BACKEND": "django_redis.ca…

【企业微信多选的星期数据生成如“周一、周三至周六“】

目标效果如下图 实现这个过程首先是要找到逻辑&#xff0c;这个看似简单的操作却属实让我想了很久。 首先要了解需求 根据拿到的数据得到生成符合要求的字符串。中间有连续的星期大于三天的&#xff0c;开始和结束星期中间要有"至"。 分析逻辑 一、判断开始日期和…

RabbitMQ系列(19)--实现在RabbitMQ宕机的情况下对消息进行处理

前言&#xff1a;在生产环境中由于一些不明原因&#xff0c;导致RabbitMQ重启的情况下&#xff0c;在RabbitMQ重启期间生产者投递消息失败&#xff0c;生产者发送的消息会丢失&#xff0c;那这时候就需要去想在极端的情况下&#xff0c;RabbitMQ集群不可用的时候&#xff0c;如…