资讯详情

C++学习笔记(第一、二阶段汇总)

文章目录

  • cmake
  • 命名空间
  • 匿名命名空间
  • C与C 混合编程
  • 引用
  • 共用体
  • inline内联函数
    • 多重定义
    • class类初步了解
    • inline函数在c 唯一多出来的特征
      • 这一特征的目的
  • c 中的NULL
    • C 中的NULL直接就是0
    • 重载函数时如何解决?
    • C 中引入了nullptr
      • NULL和nullptr的区别
      • nullptr的本质
        • C 编译时的版本设置
      • nullptr的缺点
      • nullptr和void挺类似
  • 断言
    • 断言有点像内核Kernel panic
    • 使用断言场景
    • 静态断言
      • 在模板中使用静态断言
      • 静态断言的缺点
  • 内存对齐
    • alignof测的是什么
    • 调大与调小
      • 调大 \_\_attribute__((aligned(n)))
      • 调小 #pragma pack()
        • 当调小成1时
  • C语言中强制类型转换的本质
  • typeid
    • 静态类型和动态类型
    • typeid在动态类型中,真正的大用是
  • 4种cast转换
    • static_cast的强制类型转换
    • reinterpret_cast强制类型转换
    • const_cast强制类型转换
    • dynamic_cast强制类型转换
  • const关键字
    • C中const的用法
    • C 中const的用法
      • 用法一
      • 用法二
    • const的本质
    • 与const其他关键词
      • mutable
      • constexpr
      • constinit和consteval
    • exception类
    • noexcept关键字(C 11引入)
  • RTTI在运行过程中确定对象类型的初步理解
  • 三种cast深刻理解:不是一刀切
  • 自动推导
    • auto关键字
    • declare_type
    • auto与declare_type
  • struct和class的理解
  • 静态分配和动态分配
  • this关键字
  • C 新语言特征的目的
    • const变量类似于宏定义,但有什么区别?
    • inline函数本质上提高了运行时的效率
  • 对模板的初步理解
    • C 高级语言:抽象
  • 异常处理机制
    • try catch机制
      • 为什么要用try_catch机制
      • 为什么会有异常?throw列表
  • 自由存储区
  • 内存泄漏和内存碎片
  • new和malloc的区别
    • new不能强转
    • C 基本都用new,没必要用malloc
  • 智能指针
    • 多包装一些东西,让它更智能
    • 智能指针比普通指针消耗更多的资源
  • java垃圾回收机制
    • java效率不取决于程序员,而是取决于程序员JVM虚拟机
    • 垃圾回收利用GC线程
    • 垃圾回收的两种方法
      • 引用计数法
      • 可达性分析法
  • 面向对象和面向过程
    • 理解面向对象
    • 对过程的理解
    • 将面向过程转化为面向对象
      • 版本1
      • 版本2
      • 版本3
      • 版本4
      • 版本5
    • linux内核用C语言实现面向对象
    • 面向对象和面向过程共存
    • 面向对象的核心思想不是函数指针
    • C 本地支持面向对象
    • C 从struct到class,没有变化,变化只是概念的升级
    • STM32的HAL库也是典型的用C实现面向对象的例子
    • 面向对象的本质
    • 一个namespace里面可以有多个class的
    • 面向对象C 三大开发文件
  • 构造函数和析构函数
    • 结构函数和分析函数实际上是一种回调函数(钩函数)
    • C 自动帮助您构建构造函数和分析函数
      • C\ \ 与C的区别,C 动态内存较多
    • 普通结构函数
      • 由传参引起的写作细节
        • 只要有带参,不带参的就不能省略
        • 以后用不带参的方法写的时候要去掉括号
        • 不在外面写,把中括号写入类作为内联函数
      • 为什么要去掉括号?
  • C 成员初始化列表
  • 构造函数参数的默认值
    • 参数具有默认值的好处
    • 理解的关键:匹配机制
    • C 定义对象的两种方法
  • 尽量营造对称感
  • 复制构造函数
    • 首先要注意的是:不写就代表用C 默的
  • 深拷贝与浅拷贝
    • 第一个段错误
  • 第二个段错误
    • 深拷贝与浅拷贝的命名来由
  • 类的权限管控
    • 类的内部和类的外部的理解
    • public和private
    • protected
    • 权限访问的好处
    • 访问权限的思想对架构师尤为重要
    • 访问权限牺牲了一定的效率
    • struct和class在默认情况下访问权限的区别
  • 先全部禁掉再按需打开的理念
    • struct和class交叉继承:远古大神
    • 模板之后用的全是class
    • class和struct的一个剩余的小区别
  • C的struct和C++的struct是不一样的
  • 常函数
  • multiply也遵循先全禁再按需打开的思想
  • 类不占内存
  • 类的前置声明
  • inline的进一步理解
    • 应该用不怕把类写得太长
    • inline如果写外面,要写在下面
  • 继承
    • 设计继承的本质目的:代码复用
    • 继承的本质就是结构体包含结构体
    • 继承中的访问权限
      • 类的权限管控和继承的权限管控时两码事
      • public继承
        • public继承中的private继承之后比private还严
        • 既然比private还严格,用都用不了,那为什么还有
        • 注意仍然是子类而不是父类
        • 再把该方法放进父类的private中就是死局了
        • protected如果不考虑继承的话,它跟private是一样的。
          • protected再往后走细究与private的区别,就要看继承该子类的那个子类了
      • private继承
      • protected
      • 默认继承
      • 从哲学角度看C++继承的全继承
    • 有趣的现象:打印顺序
    • 冒号的功能又多了一个
      • 子类传三个参数,父类用其中两个,使得父类传的参可以不是定值
      • 父类的构造函数和析构函数是不能继承的
      • 两种冒号的用法竟然可以放在一起混合着用
      • 当基类和派生类中出现同名方法的情况(重定义)
        • 那如果要跳过重定义机制,用父类的那个同名的怎么做
        • 正常来讲子类重名前也要用man::,只不过省略了
        • 但你要用父类的重名,就不能省略
          • 前面讲的一直是成员方法,成员变量也一样
        • 重定义的本质
    • 类型兼容规则
      • 画图理解类型兼容规则
        • 类型兼容规则对引用也是一样
        • 类型兼容规则对普通赋值也是一样
    • 不良继承
      • 解决不良继承的方法
    • 组合
      • 组合与继承的区别
    • 虚继承
      • 为解决菱形继承而存在
      • 虚继承的本质
  • 多态
    • 什么情况叫多态
    • 覆盖重写overwrite
    • 区分好覆盖重写overwrite和重定义
    • 纯虚函数和抽象类
    • 抽象类和定义对象相关的规定
    • 接口
    • 虚析构函数
      • 为什么要写虚析构函数
    • virtual的本质
    • using的打孔
      • 不用using的解决方法,新写另一个方法间接调用,但开销大
      • 使用using
    • 访问权限控制本质是编译时编译器帮你检查的,而不是运行时
  • 函数重载
    • 为什么要函数重载
    • 如何函数重载
    • 只用传一个参
      • this->和 other.
      • 前面的.是class的.,后面的点是引用&的.
    • C++完全支持,java不支持,python有限度支持
      • C++完全支持也不是所有都支持
    • 运算符重载的本质是映射
    • 运算符重载一定程度体现多态
    • 为什么返回值也要是coordinate
    • 编译器默认提供了一个等号运算符重载了
      • 为什么编译器默认提供等号运算符重载-减轻工作量
      • 初始化和赋值的区别
      • 等号运算符重载和拷贝构造函数的优先级:优先拷贝构造函数
        • 赋值用的就是等号运算符重载
          • 奇怪的一点:还打印出了拷贝构造的内容,原因:返回值值传参
          • 返回值值传参复制时调用了拷贝构造函数
          • 引出了返回值中要加入&提高效率
          • 返回值是变量,复制变量,返回值是对象,调用拷贝构造函数复制
          • 回归正题:初始化用拷贝构造函数,赋值用等号运算符重载
    • 自赋值
      • 地址一样,带来与深拷贝时类似的析构时问题
      • 解决方法:直接return *this,直接用值
          • 为什么会看到别人写等号运算符重载时都会写条件判读那,就是为了处理当有人写a=a时的情况
      • 返回值中加入&提高效率
        • C++引用的发明就是从这个等号运算符重载而来
    • C++默认的析构函数没有delete,所以一旦用了new就要自己手动写析构函数
      • 数组的delete要加[]
      • \+\+a和a\+\+的运算符重载是一个特例,先跳过
  • 友元函数
    • 运算符重载的第二种方法:友元函数,不是主次而是平行了
    • 什么时候用第二种友元函数写运算符重载
      • c++并非所有都可以运算符重载
  • 静态类
    • c++没有静态类
    • 静态成员变量和普通成员变量的区别
      • 区别一:静态成员变量需要在类外面重新定义一次
      • 区别二:静态成员变量不用创建对象就可以用
      • 区别三:静态成员变量创建的所有对象都是指向同一个,共享
        • 由此导致静态成员不能用构造函数初始化,也不能在类里面初始化,智能在类外面
          • 由此又导致静态成员变量不能用成员初始化列表初始化
    • 普通成员方法访问静态成员变量
    • 静态方法不能访问非静态的成员变量和方法
      • 硬要访问只能靠传参,但是已经很间接了
          • 变量是什么时候被分配地址的?链接时候
    • 静态成员变量和方法从生命周期角度看等同于全局变量
      • 换句话说就是通过全局变量的方式实现的
      • 普通成员变量和方法就比较像局部变量或者用堆申请出来的变量
      • 区别四:静态成员在类里面不占内存,简单说就是没有在里面
        • 就是形式上好像写上去了,但其实没有分配内存
        • 因为本质上它自身早就已经创建了内存了
    • 静态成员的用处
      • 静态成员变量的用处
        • 共享计数器
          • 即公共的东西,就用静态
      • 静态成员方法的用处
        • 处理这些共有的东西的时候,就用静态方法
      • 静态类属于一种密封类
  • 友元函数不管放到public,private,protected中都是可以的
  • 类的前置声明
  • 友元类
    • 友元类的缺点,极大破坏面向对象,编译器不能帮你排除错误了
    • 不推荐使用友元类和友元函数
      • 友元函数破坏了类的封装机制
  • 共有友元函数
  • 嵌套类(大部分时候用不到)
  • 局部类
  • 数值转对象
    • 数值转对象的本质
  • 模板
    • 静态多态与运行时多态
      • 面向对象的多态是运行时多态
    • 模板的多态是静态多态
  • 模板类
  • 模板友元函数
  • 容器类
    • array容器类
  • using在C++中的第二种用法,跟typedef差不多
  • 迭代器iterator
    • 迭代器就是一个高层次的指针
    • begin和end的左闭右开
    • 逆向迭代器
  • vector容器
  • 为什么要预留位置
    • 原因一:节省开销
    • 原因二:减少内存碎片
  • 新的遍历方法for(auto &ch : s)
  • 泛型算法
    • 容器类中本身包含了少量的泛型算法
    • 双向遗留迭代器
  • 函数对象
    • 关键是理解括号的运算符重载
  • 谓词
    • 函数对象可以用模板,但是函数不能用模板
      • 函数对象实现了静态局部变量的感觉
  • lamda表达式
    • 本质上是一个匿名函数
    • lamda函数只是写起来简便,但是效率并没有提高
    • 中括号是可以写东西的
  • 适配器
    • 适配器本质:是不同函数变成相同的传参个数
    • for each
  • 谓词补充:一元谓词和二元谓词
  • 模板的全特化和偏特化
    • 第二种偏特化的理解:特化成指针
    • 函数模板为什么不能偏特化:和函数重载功能一样,造成一定的尴尬冲突
    • 模板的全特化
      • 模板的全特化就已经是一个实例了,而模板的偏特化还不是实例
  • 迭代器萃取器
  • 三种适配器
    • 容器适配器
      • 从迭代器适配器理解C++为什么效率高
        • 大部分工作在编译期间就做好了
  • 顺序容器
  • 关联容器
    • 有序关联容器用二叉树和红黑树,无序关联容器用哈希表
    • 两大关联容器:set容器、map容器
    • set容器
      • 原地构造
    • map容器
  • 智能指针
    • 四种智能指针
    • share_ptr
    • weak_ptr配合share_ptr使用

cmake

cmake,按照它的语法描述,它可以自动生成makefile文件

命名空间

C++里面有一个命名空间的概念,就是说它那个namespace,他会把一个就是你的定义的一个变量,它是放到一个命名空间里面,也就是放到namespace里面,然后通过这样子去进行一个管理。这个是跟C语言不同的一个点,就是这个C++他有命名空间的概念,但是C语言没有这个命名空间,主要是用来干嘛的呢?它本质上设计来就是用来解决全局变量跟函数名重名的问题的,但是其实这个问题的话,C语言的本身也可以解决,但是解决的不够优雅,C语言,它解决的方法方法是他把一些函数名,全局变量啊,他在前面加一个前缀。

就比如说他在他定义的一个全局变量的前面加一个前缀,比如说他是一个串口的,那么他就加一个UART,然后一个下划线,什么什么什么意思?就是说,他是通过这种前加前缀的方式去区分不同的变量,但是这个方法的话,在小规模的C代码里面是可以用的,但是如果代码一旦庞大起来,这个还是有点难度。而且主要是还是不够优雅,那么,C++他就通过命名空间解决了这个问题,就是你你的,你可以把你的变量放到一个命名空间里面,比如说它有不同的命名空间,那么这些不同的命名空间里面的变量,就算重名了也是没有问题的。

这个命名空间,本质上它归根结底,也是去改变了他的一个最底层的编译的时候的链接属性,比如说你把你在这个命名空间里面的变量,跟另外一个命名空间里面的变量,它的链接的时候,链接的时候是不一样的意思,就是说他本质上改变的还是他链接的那个底层的一个链接的区别,这个其实也跟C是没有太大区别的,只不过C++他多了一个。命名空间的概念之后,他就这样就比较处理的就比较,方便一点,对就是你有一个命名空间之后呢,就是你这个命名空间里面的东西跟另外一个命名空间东西是相同的,也是可以的,因为最后链接的时候,他们不是链接在一块,就不会出现重名。

其实函数名跟全局变量重名的这个问题是比较严重的一个问题,因为因为最终他不管是C还是C++,他们最终都是要编译链接的,它编译链接的话,就根据这个函数名或者全局变量去找到那个东西去链接起来,所以当你的函数名跟全局变量名重名的时候呢,它链接的时候就不知道怎么链接了,所以说就是函数名跟全局变量重名的问题是一个。比较麻烦的问题必须要去解决,C的话,它就是用一个加前缀的方式去解决,C++的话就更加优雅一点,它就用了一个命名空间去解决它。但是其实本质上面归根结底还是跟链接相关的,解决的根源就是在链接时候,它是不一样的。

C加了前缀,那么它链接的时候就发现它是两个不同的变量,是不是那么链接的时候就就会按照不同方式链接,然后你如果是C++,你把它的变量放到不同的命名空间,那么它的链接的方式也是不一样的,本质上归根结底还是在他的链接的方向上面做了一个改变,最终链接不同,这才解决这个问题。

匿名命名空间

C++的那个匿名命名空间跟那个C语言里面的全局变量的static的用法很相似,就是它那个匿名命名空间,本质上就是你用了之后,那么它这个文件里面的东西,你把它放到匿名命名空间里之后,那么其他文件就不能访问这个东西了,就是这么简单。

也就是说这个匿名命名空间它没有名字的,所以就是你如果在这个C++文件里面用了这个匿名命名空间,那么你在另外一个C++文件里面是不能访问这个匿名命名空间里面的一些东西的,这是跟C语言的全局变量的static,关键字是一样的,它是可以用来一个它有一个限制的功能的,他限制你其他文件去用这些东西。

所以匿名命名空间里面放的东西一般都是他这一个,就他目前所在的这个C++文件里面,它所要用到的一些,它所要用到一些只有他要用到的东西的,只有他这个里面才需要用到的一些变量啊,函数名啊,还有一些结构体啊,还有一些累啊,都放到这个匿名命名空间里面,意思就是说这个匿名命名空间里面放的东西,只用在这个文件里面用就行了,其他文件里面没有必要用,就是这样子,他用法就是这样用。

然后他跟C语言的那个全局变量的static还有点不同,就是C语言里面的全局变量的static,它只能是用来修饰函数,还有全局变量,但是呢,那个C++的匿名空间里面不仅可以放函数变量,还可以放一些结构体,还可以放一些类,就说他这些结构体这些类,这些类型,它放到匿名命名空间里面之后,也可以实现一个。框死的作用,限制的作用,不让其他文件可以用到,它就它比全局变量的static的一个不同,就是它扩展了一点,它还可以把那个结构体啊,然后那个类呀,放到命名,放到匿名命名空间里面去,然后作为一个限制。

从一个更高的一个角度上看,这个匿名命名空间,它体现了C++的一种优雅性,就是它不算是一个特例,它不会说是一个特别的一个情况,它是可以在逻辑上面是说的通的,因为你这个匿名命名空间,它本身就是没有,本身就是没有名字的,那么你既然没有名字了,从逻辑上来讲,那你在其他文件里面自然就找不到这个匿名命名空间了,是不是?所以说他并不是一个特例,所以它也是符合匿名,他也是符合正常的命名空间的一些逻辑的。

这种没有特例,很少特例的情况,就是说他一切都是有一个逻辑的,所以这就体现了C++的一种语言实现的优雅。

C与C++混合编程

C与C加加混合编程,首先有两点先要搞清楚,第一.c加加的那个编译器居家家里面,它定义了一个下划线,C plus plus,这个东西是一个C加加编译器里面定义的一个东西,这个东西会根据的C加加C加加的版本,然后不同而不同。比如说现在的版本是118版本的,或者是八九版本啊,或者是二零版本呢?那么这个c si plus,它的这个里面的这个符号的关,它的值就是不就是不一样的,第二点就是要理解C跟C加加的那个底层的最后的链接是怎么链接的,它其实都是这样子的。 就是不管如何,她最后都是会变成一个.o文件,就是它是先从点C和点C加加文件,通过那个预处理变成一个点AI文件,点文件再通过一个编译变成一个汇编文件,就点S文件,点es文件,再通过汇编就变成了点o文件,点文件就在通过链接就变成了可执行文件,然后C与C加加混合编程,他们的编译都是到了的不同,它都是相同的,就是到了最后,这个都是可以到那个点o那里的。 然后点o文件本质上就是一个静态库文件,可以理解为它就是点I文件,然后就是点o文件,这种就是一个库文件,就是类似一个库文件,如果想要把它变成静态库或者动态库,那就去定义一下,把它定义成点I静态库或者点,So动态库就行了。 然后C与C加加混合编程的难点就在于它这个C加加,它有一个函数名重在机制,就是说它的函数名是可以是相同的,然后它中通过预处理,通过那个汇编之后等等等等处理之后,它得到了那一个可执行文件嘛,它的函数名是可以重在的,但是C是不可以的。 就比如说同样都是定义了一个ad,然后嗯,然后里面传参传的是in te gen in te,然后如果用C的话,最后出来的那一个点o文件,把它嗯,反汇编得到那一个反汇编文件,那么它里面的那个关键字还是ad,但是如果用的是C加加,那么他通过得到一个点o。文件之后,把它通过反汇编得到的那一个文件反汇编文件,它里面就不是ad了,而是ad ii。也就是说,它在它的那一个C加加,它有一个函数名重载,它最后会对一个函数名进行一个重载,如果虽然的那个名函数名是爱,但是最后它通过这个机制,它就会变成ad ii。 所以说C跟C加加的混合编程,他是有一定的难度的,就是难度的本质原因就是在于这里。所以如果要解决这个问题,那就必须要加一个extensi,就是当用C用C的时候呢,就加一个extensi,在那个extensi的那个括号里面,那么他就按C的方式去编译,如果没有在括号里面,那就是按C加加的方式去编译。 所以别人一般写C的库文件的时候,都会在他那个定义的函数前面加一个F的si si plus,然后ix ten si,原因是什么?原因就是为了防止以后如果这个C库文件,万一后面要要跟C加加混合编程的时候,那么他的这个C的话,就可以通过ex ten si,然后去实现一个C跟C加加的一个混合编程。因为通过if define c下划线cplusplus这个这个cplusplus只有C加加的编译器才有,所以当出现了这个关键字,那么就说明要用excelc就要,这就要把这个里面的东西当成C的编译器编译方法去编译,就不用函数名,重在机制。 那个点I文件是预处理后得到的文件,然后点a文件才是那个静态链接库的文件。这两点有经常会搞混,就点I才是那个预处理的文,得到预处理之后得到的文件,点a才是那个静态链接库文件,然后点so是动态链接库文件。 C加加调用C的时候呢?其实很简单,只要的C的头文件里面都用了那个if define c plus plus,然后extend c,那么它C加加调用C就很容易,但是如果是C调用C加加的话,那就没那么容易了。所以必须要得到C加加的那个反馈编得出来的那个函数名,然后这样子才能够去调用C加加,就这样才能用C去调用调用C加加。

引用

C加加里面多了一个引用的概念,就是如果是要交换两个参数的字,之前的话在C语言里面是用一个指针嘛,传地址给他改改地址,但是C加加很奇怪的是,它竟然可以传参的时候,在那个传参那里写个N的符号,然后就不用传地址,不用传地址的情况下也可以改值。 那个引用其实就是关联,然后关联的话,就是可以理解为他是一个弱化版的指针,就是他弱化了指针的功能,但是呢,他却提高了安全性,它从何体现提高安全性呢,就是它每一个引用,也就是每一个关联,他只能绑定一次变量,它后面不能再换了,就比如说是in te n de a等于。B,那么这个时候呢,那那个B就跟那个a是绑定关联起来了,后面就不能再换了,也就是其实就是个七个别名,但是这个别名呢,它一次只能他只要定义了,那一直都只能是跟那个绑定了,不能换。 所以也就决定了在定义引用关联的时候,要在定义的同时就初始化了,不能够定义的时候没有初始化,第二部在初始化,这就是这是由于它一次只能它,这是由于它永远只能绑定跟一个变量进行绑定造成的。总之这个就是一个引用,C加加引用的关联,这是C加加里面一个嗯,一个独特特有的一个点。它是一个弱化版的指针,它的目的就是为了降低指针的,它虽然弱化了指针的功能,但是却提高了安全性,然后一般用这个引用关联的时候呢,它不是正常,不是说定义了之后用的就是不是正常的情况下,这样用的,这种情况一般都常见于在返回值中,或者是在函数传参的过程中用这个引用跟关联。 关于引用和那个cos的关系,如果直接这样子cost,然后N的a等于B,然后这个时候呢,它其实就是的,如果用它引用的那个别名去改的话,改不了她的值,但是如果用它本来的那个变量,是可以改它的值的,这样子一看,似乎觉得他那个没有什么用处,但其实这个的用处是。不是这么用的,它是放到那个函数传参里面去用的,就是说当这个变量,为了安全起见,不想改它里面的值,那么就可以用这个方式,然后到了那个函数传参的时候,引用的时候呢,加一个cost,那么它就不会改到里面的值了。 这个关联的引用虽然是指针的弱化版,但是它跟指针完全不一样,就这个引用的话,它指的还是它那个,指它的size of,还是那个数据类型的大小,但是指针的话,它的size of就是那个指针地址的大小,永远都是四个字节,但是这个引用的话呢,它就是那个变量来的,它跟指针完全不一样,它没有地址的概念,它所以它还是那个数据类型的值,就比如说是double类型的,那它就是八个字节。

共用体

关于西加加的一个共用体,它的这个共用体,它有一个匿名的,一个竖一个特性,它的这个特性的主要来由就是有一些时候呢,他们懒得给这个供体起各类型了,直接就给它起个变量名了,这样的话也就说他只用一次,不用多几次了,我干脆这个供体我就只用一次,所以我干脆就只定义这个,就叫这个变量,那就可以把这个类型给省略掉了,就我不会再教他另外一个名字了,他就是也不会再用这个类型去多定义,另外一个变量,那么我就直接把这类型省了,直接就给他一个变量,就这样子,用他的变量就行了。

inline内联函数

关于inline内联函数,一般他是放到头文件里面的,他因为他在预处理的时候,他是原地展开的,它就很像那个井号提发音的那个预处理那种方式,所以他应该是放到宏定义里面啊,所以他也是放到那个头文件里面。还有就是它放到头文件里面之后,他就不用声明了,你在为头文件是在最前面的,你在一个函数这样一写,所以他这最前面,所以他就没必要声明了,所以就可以省去声明的这个步骤,所以这两点就决定了应该放到头文件里面去。

其实在C语言的时候就已经说你是online,内联函数,其实它很大程度上就等于是一个宏定义来的,它就是原地展开的,有一些函数,它也是用宏定义的方式去实现的,但是为什么要有内联函数呢?就是内联函数,它比宏定义多了一个参数类型校验,它可以进行一个参数类型的一个校验,这样校验之后就可以使用,就可以嗯,有一个多的一个,这样一个参数类型校验,它比宏定义。

之前在C语言里面没有讲的这么细过,因为函数它是可以被多次定义的,就是说他可以多次定义,为什么呢?因为它它相当于一个红来的,他在预处理的时候,它就把它有一个函数给展开了,展开到函数里面去了,所以当它展开到函数里面去的时候呢,所以他这个函数本身就已经不存在了,他已经展开进去了,跟红一样。所以你这个东西,你这个内涵是可以重复定义了,因为你每定他就,他就展开了,就放到里面去了,他就这个函数就消失了,因为没有了,那肯定可以重复定义。

关于多次定义

注意,这里指的多次定义,重复定义,它的意思是说,你定义时候它的里面的参数啊,那那参数体校要是一样的,不是说你在在那里定义的是这样,现在那里定义就是那样,这叫这就不要重复多次定义了,这个样子就就是不行的。你只能说在不同地方可以重复的去定义它,定义这定义里面的参数类型是相同的,不能不一样。

但是一般的话不建议重复定义,因为你重复定义之后你改,那你如果真的要有有一天你要改它,这个内敛函数或者是宏的话,你要改很多地方,所以你每一个定义到的地方都要改,所以这很不方便,所以最好的方法就直接用红,不不不不,所以最好方法就直接放到头文件,改头文件就行了。

但是这个多次重复定义啊,他从原则上来讲,他并没有重复占用内存,就他在占内存方面并不会造成影响,因为它最后都是要原地展开的,不存在说你多定义了一个,就多占了一个内存,它不建议重复定义的原因并不是因为占内存,也而是因为他你在维护代码,然后就是在管理代码的时候不方便,你要改的话,要找到他所有的你定义过的地方去改,这样就很不方便,所以要放到头文件是这个原因。

注意这个,原地展开,这个那个,还说原地展开它,你只是对编译器的建议而已吧,就是你是建议编译器在原地展开,但是最终能不能原地展开还是要看编译器,它自己有一时候,有些时候它是并不能原地展开的,编译器还是会把它当成函数正常函数去用的。

class类初步了解

Si家家里面的class类,它其实本质上来讲是一个简易版的一个structure,就是他C加加,他喜欢做一些封装吗?那些东西就跟之前的引用一样,所以其实class这个类呢,也是从struck的过来的,所以就是他跟stride一个唯一的区别就是struck里面的只能放函数指针,它不能放函数进去,但是class它已经可以把函数也能放进去了。但是这个的话,其实本质上来讲,这class类,它本质上里面的函数,本质上还是通过struck那个函数指针去实现的,只不过C加加帮我们做了一层,做了一做,多做了一步,所以就变得比较简易了一点。实际上这class就是从struck的演变过来的,它本质上还是一个struck的类,它是一个简易版的一个structure。

所以总结一句话,就是说class这个类,它是由struck演变过来的,它本质上就是一个struck类,只不过C加加帮你做了一些简化,使得这个class类它里面可以直接放函数了,而不是只用放函数指针,不不是,而不是,只能像struck的那种类,一样是struck,那样子只能放函数指针了。

一个正规的类的写法,正常的类的写法就是你要把函数的声明写到类里面去,然后你把函数的定义写到外面去。

然后这个类配合上那个line的话,就说你如果要在类里面用line的话,你在函数这个类外面的函数的一个定义里面,要写online,在那个类里面那个函数的声明也要写inline,但是其实从后面的话,你可以在那个类里面的online把那online省略掉,但是函数定义那里的online是不能省略的。

这音量函数呢,它有个特点,就是你如果要是在一个C文件里面定义了的话,它就只能在那个C文件里面去使用了,它跟红是一样的,你所以你如果不把它放到头文件里面,你把它放到C文件里面的话,它就只能在它放到那个C文件里面去使用的。

inline函数在c++中唯一一个多出来的特性

依赖函数在C加加中的多维一多一个特性就是就是C加加里面啊,它的类里面是可以写函数的本体的,就是正常来讲的话,应该是只放一个函数声明就行了,然后,然后他C加加编译器肯定会在编译器阶段把它转化成指针啊,不转化成函数指针,但是你如果是在这个类里面呢,放一个函数的一个定义,有函数体的话,虽然很奇怪,但是其实编译器也是可以做的。就是编译器,它也会把你这个函数体定义的,这函数体,它也会去找到一个它的函数指针去执行它,这样也是可以做的。然后它跟in line的关联,就是,如果你一旦把这个函数体函数的定义放到了类里面的话,那么它就默认它是一个内联函数了,就是一个in line函数了,这个时候你就算不加inline关键字,它也是online函数,所以这时候online是可以省略掉的。

这个特性的目的

Si加加他为什么要这么设计,其实本质上也是为了提高它的简便性跟效率,因为它这个本质上来讲,它就是深度绑定的嘞,因为这个函数跟这个类就是深度绑定了的,你这种情况下,你何必不加inline,这何必不用依赖呢?所以他应该就是用依赖的,所以这就是省去了我们的一些麻烦,就是他已经是本质上本身就深度绑定了,那就是依赖吗?是吧,如果你没有这个规则的话。那么你就要把你的那个类,还有你的那个银赖函数,再写在外面的那个依赖函数都要放到那个头文件里面去,然后你这就挺麻烦的,你后面的话调调调起代码来是有一定的麻烦。所以就是从一个代码的开发的性开发方便性来说,他这么搞的话,是本身是深度绑定的,你这么一做,那就是非常的切合。

也就是说,你如果是把它的内联函数放到这个类的外面,其实你在用的时候呢,你这两个都是要一直放到一起的,要一起放到同个头文件里面的,不然的话那类找不到那函数嘛,是吧,但是你这么一想到,它既然一直都放一起,那还不如那C加加编译器就想还不如就把它干脆就缩进那个类里面去,这样写代码多方便,少写了多少,写了几行,所以本质上是这这样除法的设计的。

c++中的NULL

C和C++里面的NULL是不一样的,C里面的NULL,它本质上是一个(void *)0,比如说你定义了一个

int *p = NULL;

那么他这个NULL,他用宏展开之后就变成了(void *) 0,它在C语言里面是可以的,这个(void *)0它会隐式转换成int,到时候它会转换成int *的,但是C++里面呢,它不行,为什么他无法这样子。把这个(void *)隐式转换成int *,因为C++它的类型的检验比C还要强,因为C++它的复杂程度比C高了一点,它就更容易触发一些稀奇古怪的错误,所以它在这里就更加要注意,它不允许你有这这样一个程度的自由度,所以它为了一个保证安全,就必须得要把这个禁掉,就不允许这样子用。

C++中的NULL直接就是0

C++里面他直接NULL就是个零了,所以int *p等于0,但是这也很奇怪,它也没有强制类型转换了,按理说你C++如果是一个强类型的话,你这不是也不行,但就是可以,他就是你把那定义成零了,那就是他可以,他这种情况下,它就可以把它这个零隐式转换成int*,就是这样子,你如果单单纯把它定义成那等于零,它可以隐式转换。

函数重载时怎么解决

在C++里面,它是允许函数重载的,函数重载的意思就是说,他有可能有两个函数,它的函数名是一样的,但是它里面传的参数是不一样的。然后你在用的时候,它C++就会是,就是看你到时候用了这个函数名里面,你传的参是什么,然后去找它对应的函数。比如说一个传参是普通变量,另一个函数传的参数是指针变量,但他名字是相同的,那你在用的时候呢,你如果传的是一个指针,那么他就会找到那个传指针的那个函数名。如果你用的是一个传变量,那他就找到传变量那个东西,那个函数,函数名是一样的,这就叫做函数重载,它本质上还是刚刚还之前说了C跟C++混合,编程里面它有一个预处理,C++的预处理,它会把它的函数的那个传参,比如说它是int int,它就会变成ii,所以它两个函数,其实本质上在预处理里面已经变得不一样了。

所以说函数从在本质上还是预处理的时候,C++在预处理的时候,它把这函数名也变了。所以本质上讲,这函数你就算写的时候是一样的,但它在预处理的时候,它已经函数名是不一样的。

C++里面,他这个NULL这么定义,它有一个问题,就是在那个函数重载的时候,比如说你这函数重载,你的第一个是第一个传的参数是int类型,第二个传的参是那个int *类型,那你最后你再调用这个函数的时候,你给他传个NULL,那他到底是int类型还是int *类型呢,所以C++在这个时候呢,他为了避免引起歧意,他就在这里,就选择了报错,就不让你这么做。

C++中引入了nullptr

然后C加加为了避免这种歧义,他就发明了nullptr,你用这个的话他就不报错了,你用这个他就默认你这个用的是指针了,那你就是函数重载的时候呢,他就是用传的是指针的那一个函数。

NULL和nullptr的区别

这个nullptr的话,他跟NULL一个区别就是NULL,他是一个宏来的,但是nullptr是一个关键字来着,它就是发明了一个空指针给你用。

所以其实在C++里面的话,你定义一个指针,然后让它指向空的时候,那就不应该不应该是用NULL,而是用nullptr,比如说char *p等于nullptr,而不是做成NULL,因为他是在C++里面,NULL是一个宏,他直接就是个数字零,这是不对的,,但是也是不太恰当了,因为他那是零,然后char *p的话,它是指针。虽然C++里面它是可以允许你这样编译通过的,但是其中严格上来说,你应该是用用C++里面它给你的关键字nullptr。nullptr就是C++里面为了解除歧义,然后专门发明了一个关键字。

nullptr的本质

这个nullptr的本质是一个类来的,就是你定义出来的一个类,它这个类里面呢,有一个在里面它会返回一个零,然后它重载呢,是把它变成了一个指针,所以说这个nullptr的本质就是你定义了一个类,然后这个类,它的类型是指针类型的。

const class nullptr_t
{ 
        
    public:
        template<class T> inline operator T*()const { 
        return 0;}
        template<class C, class T> inline operator T C::*() const { 
        return 0;}
    private:
        void operator&() const;
} nullptr={ 
        };

C++的一个编译时的版本设置

在C加加里面,你去进行编译的时候,你可以选择你要编译的版本,就是说你在后面加STD等于什么什么,比如说你要九九版本的STD99,或是STD等于一三呐,你用一三版本之类的,就是说他C加加可以按不同版本去编译的,按你需要的版本去编译,你就后面加STD等于什么就行了,编译的时候。

nullptr的缺点

其实这个nullptr它没有解决掉实质的问题,它说可以解决那个变量跟指针变量的一个重载的一个冲突问题,但是如果它的函数重载两个都是指针的,那这样的话,它nullptr也是无法解决的,就是说它两个指针nullptr也不知道你重载的是哪一个。

nullptr和void挺类似

其实这个nullptr他有点和void相似,因为他你看我们之前在C里面用的是void *,它可以是任意的一个数据类型,都可以,他到时候根据选择去做,然后这个nullptr的话,它其实也是跟void很相似,他都可以支持,就是你是哪一种类型都可以,到时候我都可以支持,你到时候是哪一个,nullptr就变成哪一个。

断言

断言这个东西就是他。他不是在编译时出错,它是运行时你程序编译是成功的。你运行的时候,只要你运行的过程中一旦触发了这个断言,那你程序那里去就停止了。就是这个意思。

断言有点像内核的Kernel panic

断言有点像那个C源里面,内核里面那个kernel panic那个kernel panic就是你一旦遇到了,就直接运行的时候,就就就在那里断掉,就是这样子就停止。

这个断言的使用场景其实也是跟Linux内核的kernel panic一样的,你用它,那就代表你这个东西是你无法接受的一个错误了,一定要在这里卡住了,终止掉了才用断言。比如说你在打开一个文件系统文件的时候,你你一般情况下用不上断言,就比如说你这个文件没有打开成功,你返回一个那,那么你就可以用衣服闹一幅他。等于闹,那么你就反,你就打印一个什么error或者是一个重新来一次,重新做一次打开,是吧,就是你会有一些其他系列的操作可以弥补,如果你在这里直接写了一个断言的话,那程序你就是,就是认为你是认为这个是很严重的事情啊,就直接就把这个程序在这里就终止掉了,所以断言一般是这个比较紧急情况下,你认为紧急才用的。

断言的使用场景

这个断言呢,一般用在什么场景呢?就是用来解,就是用在你文件的最开头,你这个代码最开头,你传参传进来之后,他传参是否是正确的,一旦传参都不正确,你就只用断言了,你就直接就是开头就断言掉,它就终止掉,一般都是在这里用,就在函数开头传参的时候用。

静态断言

这个C语言里面的断言他是在运行时报错,但是其实运行时报错是不太理想啊,最好的情况就是你在编译时就报错了,所以C加加里面它就引入了一个静态断言的这样一个东西,就是你这个静态断言就是一个家一个static下划线,再来一个assert,然后这个静态断言C加加里面静态断言的使用就是你用这个static,下划线assert,然后括号。里面写上你要判断的表达式,然后后面写上你要出报错的一个打印出来的东西,你写上这个之后呢,一旦你的表达式里面的那个不符合条件,那么他就会打印出它,在编译的时候就可以打印出错误了。他跟C语言就的断言不一样,它编译时就可以打印出错误了。

静态断言在模板中的用处

然后这个C加加里面的静态断言,我们在平常用的地方比较少,因为没有这个必要,我们直接,因为我们都一般都知道吗,比如说他,你他一般是用来做一些校验的吗?我们一般肉眼都可以看出来他到底对不对,是不是,所以一般用不到,所以他这个用的最多的C加加的静态断言用的最多的地方是在他模板那里,因为他C加加的模板里面,他的一些函数类型是比较模糊的,你不知,你很很难去判断他这个函数它的传的参数到底是什么类型,模式很模糊的,所以这个时候你就需要有一个静态断言去用它。

这里静态断言就是用来,就是一般是那些写模板的人,而不是用模板的人去做的。就是那些写模板的人,他本身自己是清楚的,他是怕你那些用模板的人,他可能没那么熟悉,所以他写模板的时候就提前预想到可能会有问题,就帮你在那里写,加了一个静态断言,然后你你到时候你用模板的那些人呢,那么你就可以哦,有个静态断言在那里,你就可以大概知道错哪里,否则你连静态断言都没有的话,你用模板,那些人一旦运行时候出错,你很难查找出问题所在的。

静态断言的缺点

但是这一个C加加里面的静态断言他有个不好的地方,就是它打印出来的报错,它其实是很抽象的,就是没有那么明确的,你可能最多就只能知道他那里有一个静态断言打印出来出错了,但是你可能并不知道他,你不能通过他的文字信息得到它到底是什么问题的,什么类型的错误,然后这个静态断言他是C加加11里面出现的,它的C加加11里面出现的一个问题,就是这个问题,就是他打印的那个信息,很难,通过这个信息去判断出它是什么类型的错误。然后到了C加加20,然后它又有多一个关键字叫concept concept concept concept,就是你可以理解为就是那个静态断言的一个进一步的一个版本,它的话打印出来的信息可能就比静态断言更好理解一点,就是这样子。

总的来说,这个嗯,静态断言,它主要就是用去后面的模板的一些他的模板里面的一些参数,它的是否是常规合理的,你因为你模板那里不不清晰的,所以你可能一些参数的一些东西不是太合理的,所以你要通过一个静态断言你开发模板的人,你要写模板的时候,给他一个静态断言进去,用模板的人才能够大概清楚至少有一个方向在那里,至少至少知道他这里是错的,但是什么清清不清楚,是什么类型错,他文字信息是很难够推断出来的,但是至少知道有个错。

内存对齐

alignof测的是什么

alignof测的是结构体中你定义的变量的最大的那个变量类型所占的字节的大小,这个是错误的,这个没有看到本质。。所以由这点你也可以推断出来,其实,你在结构体中写了double后,alignof测出来是8,那么其实它就是按8字节去对齐的,而不是按4字节对齐的,所以可以得出结论,这个不是死的,不一定一直都是4,只有结构体里面最大的那个是int的时候才是4,而如果结构体里面最大的那个是double,那么就是8个字节了,而事实上,这个不仅不固定,而且还是手动可调的,下面就讲了如何手动调。

调大与调小

这个有关C加加的内存对齐,嗯,它这个里面就是首先它有一个a line的这个东西就是他跟size of,一般这个课程里面是拿了a line跟size of一起去用,一起去对比这个aline的话,它就是用来查看你这个这个结构体的,对其的最小的一个单元,就是你设置好的结构提的一个对齐的最小单元。然后size of就它整个结构体的大小嘛,然后你一般是用aline去看的,如果你没有,然后这个结构体内内存的最最对齐的最小单元,你是可以调的,你往大了调,你就要你就可以用一个那个aline的那个a line a of什么的一个东西,然后你去调,然后给它可以给它设置的。

然后你往小了调,可以用一个parent,然后什么packed,然后往小了调,把它那个结,那个结构体的那个最小,对其单元往小了调,就是意思就是说,嗯,它有一个可以查询你结构体对齐的一个最小单元的一个东西,叫line的line of,然后你可以把你可以人为的把它结构体内存对齐,往大了调,往小了调往大了调,你就用一个那个东西往小了调,你用那个pad。

调大 __attribute__((aligned(n)))

调完之后,你在用alignof去测,就会发现得到是最大的那个是你设置的那个大的了,比如之前最大的是8,你手动设置成32了,那就变成32了。 就是你往大了调,就是用那个attribute,下划线是用了下划线,下划线,Attribute,然后括号,Aline的,然后里面你写你要写的那个,往大调调到多大,然后就你对齐的那个最小单元往大了调调多大,然后就是这样。

调小 #pragma pack()

这个往小了调,也会把alignof测的那个值给改变了 然后你往小了调,就是用那个一个井号,然后一个paragraph pragma peck,然后括号往然后里面填数字,一般是上面有一个井号调调,完数字之后下面再写一个井号,然后把那个东西置空,里面括号变空,这样的话,你下面的那个就不会变,不会变,不会也受到你的影响,只有上面中间夹住的那部分受到影响。

他这个往小了调,就是有个夹层,你你上面一个井号,下面井号,上面井号写的你要调大小,最小多调多少,你下面那个井号就给他一个空白,就是夹到这里,然后,这里面的东西就是调的,这里面几个题,就是你要调的,在这里面之外的其他的几个题就不受这个影响。

当调小成1时

当调小成1时,你会发现sizeof也变了,直接就是有多少个字节,就是多少个字节了,这个情况其实就是按1字节对齐了,所以内存不存在空出来的情况了。

这里我们试了之后往大调,要用那个才行,然后往小的调用那个才行。然后这个的话不一定是绝对的,可能是我们没有注意到某一些细节的语法,说不定改一改他们也可以,大的可以往小调,小的也可以往大调,但这里就不深究了。

再往小了调的话,是上面那个井号填数字下面那个井号填空就是夹层嘛,它上面有个井号,下面有个井号都写了一个pack parent的pack,然后上面的井号是填数字,你要设定的数字,然后下面的井号就是空,下面井号就不用设置的是这样子的,下面井号的数括号里面是空的,不用设置数字,就上面那个井号,你的那个里面的package的括号里面采用设置你要设置的大小。

内存对齐,还有一种往上调的方法,就是用那aline as aline as的话,它是写在那个struct定义的那个,那那句话里面的,你比如说你是struct什么什么什么是吧,然后加个变量名是吧,然后你那个ali

标签: da4y变送器02重载连接器he1414zb4m连接器1619zb4m连接器8p8孔连接器扣沉板式连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台