当前位置: 首页 > news >正文

侯捷课程笔记(一)(传统c++语法,类内容)

侯捷课程笔记(一)(传统c++语法,类内容)

2023-09-03更新:
本小节已经完结,只会进行小修改
埋下了一些坑,后面会单独讲或者起新章节讲

最近在学习侯捷的一些课程,虽然其中大部分内容之前也都已经了解过了,不过还是收获颇丰,特别是独立与所谓语法之外的,还有许多与设计相关的。

这一章内容比较简单,也就直接摆出内容吧, 相关重点内容就提一提,大部分都直接掠过了。然后会加入一些自己的侯捷没有讲到的内容。

那就直接开始吧


类设计时头文件防止重复包含

通常用法就是

#ifndef FOO_H__
#define FOO_H__
// ... 主体内容#endif // FOO_H__

因为这个很常用,但是每次都要写三行,也就有了简化版本

#pragma once

不过这个可能需要编译器支持(大部分肯定都是没问题的)


类内成员函数默认就是inline

inline就是内联,inline允许我们在头文件中定义函数重复包含时而不会发生重复定义问题
由于很多时候我们写类是在头文件中(声明在头文件),这时候,如果成员函数定义在类内,不也就是把函数放在了头文件吗,编译器比较智能,也就默认给类内定义的成员函数自动加上了inline属性
如果我们要把类的成员函数写在类外,就没有inline这个属性了,这时候如果还在头文件,我们就要手动加上inline


类使用初始化列表对成员进行初始化

初始化列表其实也就只有两个需要注意的点:

  1. 初始化的顺序不是按照写的顺序来的,而是按照成员变量定义的顺序来的
  2. 如果父类没有默认构造函数,也就是子类必须显式调用函数初始化父类,这时候也就必须要使用初始化列表初始化父类

类内对函数加上const(常函数)

正常的成员函数是可以更改类的,所以对于一个const属性的类,编译器考虑到这个类不可以更改,也就不允许它调用普通的成员函数,只能调用常函数(带const属性的函数)
一个良好的变成习惯是:对于一些不会更改成员变量/类属性的函数,都应该加上const属性,比如获取变量GetVal类似的函数

ps:使用const实例化一个对象,这个类一定要有用户定义的构造函数


对于第一点,比如下面的代码:

struct A {int a;int b;A() : b(1), a(2) {}
};

在这里初始化的顺序实际上是先执行a=2,再执行b=1,因为a是先定义的那个
看起来好像无所谓,但是下面的代码

struct A {int a;int b;A() : b(1), a(b) {}
};

就会出现a先初始化,但是这时候b还没有初始化的问题


对于第二点,比如

struct Base {int a;Base(int){}
};
struct Derive : public Base {Derive(){};
};

父类没有默认的构造函数,子类必须要也只能通过初始化列表初始化父类

struct Derive : public Base {Derive():Base(10){};
};

对于一个类来说,常用的构造函数或者基本架构是什么样的

在侯捷的课程中,常写的是

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值函数

其中如果类中包含指针,常常会自己写拷贝构造和拷贝复制,并会在析构函数中对指针进行处理

而在c++11后引入右值的概念
增加了

  • 移动构造函数
  • 移动复制函数
class MyClass {public:MyClass();MyClass(const MyClass &) = default;MyClass(MyClass &&) = default;MyClass &operator=(const MyClass &) = default;MyClass &operator=(MyClass &&) = default;~MyClass();
};

类中使用友元和重载cout

我们通常会这么写,并且也可以直接把重载函数写在类内

struct A {public:friend std::ostream& operator<<(std::ostream& os,const A& obj) {os << obj.a;return os;}private:int a;
};

可以注意几点:

  • 加上friend就可以访问类中的私有变量
  • 输入的os没有加上const,是因为os执行<<会更改内部内容,无法使用const
  • 输入的引用是为了避免不必要的拷贝,obj加上const是加上&后也可以传入右值
  • 返回引用是可以使用链式编程

带有指针的类的结

如果类内带有指针,比如类的构造函数里会new一块内存。

  • 需要在析构函数内释放对应的内存
  • 需要注意深拷贝和浅拷贝的问题
    • 拷贝构造函数和复制构造函数需要重写,重新开辟内存来进行深拷贝
  • 在赋值构造前需要注意是否是自身赋值(自己赋值给自己),需要判断这种特殊情况以防止bug

比如下面这个示例,注意关注一下前面提到的几点

template <typename T>
struct A {public:A(int n) {size = n;str = new T[n];}~A() { delete[] str; }A(const A& obj) { Copy(obj); }A& operator=(const A& obj) {if (&obj == this) return *this;Copy(obj);return *this;}void Copy(const A& obj) {if (str) delete[] str;size = obj.GetSize();str = new T[size];std::memcpy(str, obj.str, size * sizeof(T));}int GetSize() const { return size; }private:int size = 0;T* str = nullptr;
};

内存分配和管理

这一部分其实单独属于一块内容,后面会单独讲


设计模式(单例模式或者其它)

这一部分其实单独属于一块内容,后面会单独讲


类转换成标准类型(operator int())

类可以使用operator转换成一些类型,在编辑器认为转化可以通过编译时会进行转换,当然我们也可以使用static_cast显式转化

struct A {public:A(int a_) { a = a_; }int a;operator int() {return a;}operator float() {return a;}
}
void Test() {Aa(10);int b=10+static_cast<int>(a);int c=static_cast<float>(a)+100;
}

需要注意的是,这个operator函数并不需要返回值,默认返回值类型就是你写的要转换的类型
这里operator不光可以转为内置类型int/float,还可以转换成自己写的类等等

类通过单参数的构造函数自动转化/explicit

c++可以通过operator将类转化成一些类型,同样也支持反向转化,编译器可以自动根据单参数输入的构造函数,将对应的参数的自动执行构造函数构造对象
💡:这里说的单参数,只指可以输入是一个参数的函数,比如一个函数有三个三数,但是后面两个带了默认参数,也满足单参数;又或者只有一个参数,并且这个参数是默认参数,也属于单参数

struct A {public:A(int a_) { a = a_;}int a;A operator+(const A& obj) {return A(a+obj.a);}
};

比如我要执行: A a(10); A b=a+10; 这里重载了+运算符,c++会自动将后面的10调用构造函数,转换成一个类

如果是A b=10+a就不行
这里的+运算符属于内置的int,在这里就不会默认转换
如果要运行这句话,就要用前面的operator int()

operator int() {return a;
}

运行的顺序就是先把a转换成int,然后10+整数,然后将加之后的结果转换成A类类型赋值给b

如果我们同时写了单参数的构造函数和operator 类型(),就有可能出现冲突,就比如上面的例子:


struct A {public:A(int a_) { a = a_;}int a;A operator+(const A& obj) {return A(a+obj.a);}operator int() {return a;}
}void Test() {A a(10);A b=a+10;A c=10+a;
}

这里的A b=a+10;就会有问题,因为冲突了

  • 因为既可以把10调用构造函数转换成类然后执行operator +
  • 也可以把a转换成int,然后加了后再调用构造函数转换成类

这时候我们就可以使用explicit显式的禁止允许单参数的构造函数的默认转换,这样那个构造函数就只允许我们显式调用,而不允许转换了。在示例中也就是不允许10转换成类的类型了


让类表现出指针形式(重载*->

我们可以重载*->让类表现出类似于指针的形式,使用示例比如说智能指针和容器的迭代器

struct A {public:int& operator*() const {return *p;}int* operator->() const {return &(this->operator*());// return p;}int* p;
};

在这里,*用来表现解引用,->用来表现指针的成员内容,一般来说->在函数返回值表现形式后还会有一个潜在的->(设计如此)


让类表现出函数形式(重载括号运算符)

这个其实非常常用,比如在ceres库中用于传递代价函数,平时也把这种函数叫做仿函数(模仿函数?)

struct A {public:template <typename T>T operator()(const T& a,const T& b) {return a>b?a:b;}
};
void Test() {A a;std::cout << a(10,20);
}

函数模板/类模板/模板模板参数

对于模板来说,关键字classtypename是一样的,为啥有两个?历史原因,不重要了
函数模板:

template <typename T>
void Foo(T t) {}

类模板:

template <typename T>
struct Foo {Foo(T t) {}
}

模板模板参数:

template <typename T>
struct Foo {T foo;
};template <typename T,template <typename> class my_class>
struct A {my_class<T> class_a;
};template <typename T1,typename T2>
struct Goo {T1 goo1;T1 goo2;
};
template <typename T1,typename T2,template <typename,typename> class my_class>
struct B {my_class<T1,T2> class_b;
};void Test() {A<int,Foo> a;B<int,float,Goo> b;
}

其中:template <typename> class my_class是一个示例,表示输入的是带有一个模板参数的类

  • template表示这是一个模板
  • typename的个数表示对应类的模板个数,需要和传入的模板类对应
  • class 也是个关键词,和typename一样,换成typename也是可以的
  • my_class是这个模板名

在上面的示例中,分别给了一个参数和两个参数的模板类传参示例


模板特化和偏特化

其实特化也就是指定模板的某一个或者任意个参数。
所以特化也就分成全特化和偏特化,全特化就是所有模板参数都指定的特化,偏特化就是只指定部分的特化

有一条规则是函数模板只允许全特化,不允许偏特化,类模板允许偏特化

比如在c++标准库中判断一个参数是否为整数的源码:

// Integer types
template <typename _Tp>
struct __is_integer {enum { __value = 0 };
};
template <>
struct __is_integer<bool> {enum { __value = 1 };
};
template <>
struct __is_integer<char> {enum { __value = 1 };
};
template <>
struct __is_integer<signed char> {enum { __value = 1 };
};
template <>
struct __is_integer<unsigned char> {enum { __value = 1 };
};
...
后面有其它整数类型包括了intlong,都是和前面一样的
...

在这里列出所有整数类型,只要是整数就会进入到特化的版本,这样只需要根据__value的值就可以判断了
上面这种形式就是全特化

再给个函数模板全特化的例子:

template <typename T1,typename T2>
void Foo(T1 a,T2 b) {std::cout << "Test1" << std::endl;
}template<>
void Foo<int,float>(int a,float b) {std::cout << "Test2" << std::endl;
}void Test() {Foo<int,int>(10,20);Foo<int,float>(10,20);Foo(10,20);Foo(10,20.0f);
}

下面演示下类模板偏特化和全特化

template <typename T1,typename T2>
struct A {A(T1 a,T2 b) {std::cout << "A" << std::endl;}
};template <typename T>
struct A<int,T> {A(int a,T b) {std::cout << "A<int,T>" << std::endl;}
};template <typename T>
struct A<float,T> {A(int a,T b) {std::cout << "A<float,T>" << std::endl;}
};template <>
struct A<int,int> {A(int a,int b) {std::cout << "A<int,int>" << std::endl;}
};template <>
struct A<int,float> {A(int a,float b) {std::cout << "A<int,float>" << std::endl;}
};void Test() {A("10","20");A(10,"20");A(10.0f,"20");A(10,20);A(10,20.0f);
}

在上面的例子中就是给了两个偏特化和两个全特化

通过前面的全特化都可以看出来,全特化一般会伴随template <>出现(因为全都指定了,也就没有T类型了)


auto/右值引用/变长模板 等c++11的内容

这些内容后面都会单独讲,而且其中很多内容在c++14/c++17会有变化和增强
比如右值在c++17后定义更加成熟,变长模板增加了一些展开方式
后面单独讲比在这里粗浅的讲要好


继承/虚函数/虚表

这一块最好也是单独开一块内容来讲,关于虚的内容还是很多的。底层实现也很值得研究。


相关文章:

侯捷课程笔记(一)(传统c++语法,类内容)

侯捷课程笔记&#xff08;一&#xff09;&#xff08;传统c语法&#xff0c;类内容&#xff09; 2023-09-03更新&#xff1a; 本小节已经完结&#xff0c;只会进行小修改 埋下了一些坑&#xff0c;后面会单独讲或者起新章节讲 最近在学习侯捷的一些课程&#xff0c;虽然其中大部…...

自动化安装Nginx脚本:简化您的服务器配置

在如今的网络世界中&#xff0c;Nginx作为一款高性能的Web服务器和反向代理服务器&#xff0c;扮演着至关重要的角色。然而&#xff0c;手动安装和配置Nginx可能会耗费大量时间和精力&#xff0c;特别是对于那些对Linux系统不太熟悉的人来说。幸运的是&#xff0c;我们为您带来…...

通过es索引生命周期策略删除日志索引

通过es索引生命周期策略删除日志索引 在es 7.x版本之后&#xff0c;多了个索引生命周期的概念&#xff0c;可以一系列的设置&#xff0c;给新生成的索引绑定生命周期策略&#xff0c;到期后&#xff0c;索引自动删除。 也可以通过linux定时任务实现&#xff0c;请查看另一篇文章…...

网络实验 VlAN 中 Trunk Access端口的说明及实验

网络实验 VlAN 中 Trunk Access端口的说明及实验 VlAN 虚拟局域网技术Access端口 工作原理Trunk端口 工作原理简单实验&#xff08;一&#xff09;划分不同Vlan&#xff0c;实现vlan内部通信拓朴图主机IP 配置划分Vlanping 测试 &#xff08;二&#xff09;跨交换机实现VLAN间通…...

打包个七夕exe玩玩

前段时间七夕 当别的哥们都在酒店不要不要的时候 身为程序员的我 还在单位群收到收到 正好后来看到大佬些的这个 https://www.52pojie.cn/thread-1823963-1-1.html 这个贱 我必须要犯&#xff0c;可是我也不能直接给他装个python吧 多麻烦 就这几个弹窗 好low 加上bgm 再打包成…...

ReactNative 井字游戏 实战

效果展示 需要的插件准备 此实战项目需要用到两个插件。 react-native-snackbar 底部信息提示组件。 react-native-vector-icons 图标组件。 安装组件&#xff1a; npm i react-native-snackbar npm i react-native-vector-icons npm i types/react-native-vector-icons /…...

五-垃圾收集器G1ZGC详解

回顾CMS垃圾收集器 G1垃圾收集器 G1是一款面向服务器的垃圾收集器&#xff0c;主要针对配备多颗处理器及大容量处理的机器。以及高概率满足GC停顿时间要求的同时&#xff0c;还具备高吞吐量性能特征 物理上没有明显的物理概念&#xff0c;但是逻辑上还是有分代概念 物理上分…...

opencv入门-Opencv原理以及Opencv-Python安装

图像的表示 1&#xff0c;位数 计算机采用0/1编码的系统&#xff0c;数字图像也是0/1来记录信息&#xff0c;图像都是8位数图像&#xff0c;包含0~255灰度&#xff0c; 其中0代表最黑&#xff0c;1代表最白 3&#xff0c; 4&#xff0c;OpenCV部署方法 安装OpenCV之前…...

k8s etcd 简介

Etcd是CoreOS基于Raft协议开发的分布式key-value存储&#xff0c;可用于服务发现、共享配置以及一致性保障&#xff08;如数据库选主、分布式锁等&#xff09;。 如&#xff0c;Etcd也可以作为微服务的注册中心&#xff0c;比如SpringCloud也基于ETCD实现了注册中心功能&#…...

分页功能实现

大家好 , 我是苏麟 , 今天聊一聊分页功能 . Page分页构造器是mybatisplus包中的一个分页类 . Page分页 引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</ver…...

普通制造型企业,如何成就“链主品牌

“链主品牌”通常掌握产业链主导地位&#xff0c;对于普通制造型企业看起来是遥不可及的事情&#xff0c;事实上并非如此。从洞察穿越周期的“链主品牌”规律来看&#xff0c;做螺丝起家的伍尔特、做宠物牵引绳的福莱希等小企业也可以成为“链主品牌”。另外&#xff0c;由于新…...

04_22 vma(进程下的每个虚拟内存区域查看)对象实战

前言 vma不太懂的可以往前翻 03_008内存映射原理_虚拟内存区域vm_area_struct详解,和mmap系统钓调用及物理内存结构体完全分析 vam 虚拟内存区域 每个进程下有多个vma 这次是查看每个vma的起始地址 结束地址和大小使用 1.进程在用户空间调用mmap也就是上面那个函数。 2.在当前…...

QWidget的ui界面绘制成图片

文章目录 源文件源码解释效果修复图片清晰度 源文件 #include "widget.h" #include "ui_widget.h"#include <QPixmap> #include <QDir>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 构造…...

【ICer的脚本练习】脚本使用的思维培养 —— 用例回归

系列的目录说明请见:ICer的脚本练习专栏介绍与全流程目录_尼德兰的喵的博客-CSDN博客 前言 脚本思维本质上是对重复人力操作的汇总与提炼,也就是说如果一件事情可以通过人不需要大量思考的按部就班操作能够完成,那么理论上脚本就可以进行替代。那么什么时候需要写脚本呢?这…...

【axios网络请求库】认识Axios库;axios发送请求、创建实例、创建拦截器、封装请求

目录 1_认识Axios库2_axios发送请求3_axios创建实例4_axios的拦截器5_axios请求封装 1_认识Axios库 功能特点: 在浏览器中发送 XMLHttpRequests 请求在 node.js 中发送 http请求支持 Promise API拦截请求和响应转换请求和响应数据 2_axios发送请求 支持多种请求方式: axios…...

Android——基本控件(下)(二十一)

1. 数据存储 1.1 知识点 &#xff08;1&#xff09;掌握Android数据存储的分类&#xff1b; &#xff08;2&#xff09;可以使用SharedPreferences存储数据。 1.2 具体内容 对于我们数据的存储而言&#xff0c;Android一共提供了5个数据存储的方式&#xff1a;SharedPrefe…...

websocket基础

下面就以代码来进行说明 1&#xff0c;先导入websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.编写websocket相关bean管理配置 Config…...

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)

前期介绍 我是一名逆水寒的玩家&#xff0c;做一些游戏的笔记当作攻略记录下来&#xff0c;荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关&#xff08;非逆水寒老兵服&#xff09; &#xff08;1&#xff09;老一&#xff1a;巨鹰 1&#xff09;机制一&#xff1a;三阵风…...

【数据蒸馏】静态数据蒸馏方法汇总

基于几何的方法 基于几何的方法假设在特征空间中彼此接近的数据点往往具有相似的属性。因此&#xff0c;基于几何的方法试图移除那些提供冗余信息的数据点&#xff0c;剩下的数据点形成一个核心集合S&#xff0c;其中jSj ≤ jTj。 Herding&#xff08;聚集&#xff09; 是一…...

Cortex-A7 架构

参考《 Cortex-A7 Technical ReferenceManua.pdf 》和《 ARM Cortex-A(armV7) 编程手 册 V4.0.pdf 》 【 正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6学习 1.Cortex-A7 MPCore 简介 I.MX6UL 使用的是 Cortex-A7 架构&#xff0c;Cortex-A7 MPcore 处理器支持 1~4 核&#…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

如何做好一份技术文档?从规划到实践的完整指南

如何做好一份技术文档&#xff1f;从规划到实践的完整指南 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...

虚拟机网络不通的问题(这里以win10的问题为主,模式NAT)

当我们网关配置好了&#xff0c;DNS也配置好了&#xff0c;最后在虚拟机里还是无法访问百度的网址。 第一种情况&#xff1a; 我们先考虑一下&#xff0c;网关的IP是否和虚拟机编辑器里的IP一样不&#xff0c;如果不一样需要更改一下&#xff0c;因为我们访问百度需要从物理机…...