防止头文件重复包含 1. #pragma once 系统自带的方便,但是有些编译器不能用 2. #ifndef A 如果没定义 则定义 #define A
C与C++混合编程 c语言中,函数 a和全局变量 b 会被编译成 _a和_b 这种格式
c++中,函数run和全局变量var会被 编译成类似?run@@YAXXZ 和 ?var@@3HA这种格式 为什么要函数名重整?(为了支持函数名重载机制) 解决办法: 在头文件中写入
利用__cplusplus宏定义 在cpp中生效 在c中无用
新的类型名重定义语法:using 在定义一般类型的别名时效果和typedef一样 Using it=int; 在定义模板的别名时,只能使用using! 例:第一个情况则会报错
类型名推导 auto :会自动识别变量类型 增强for循环: For(auto i:arr):用于更方便的遍历数组
2021.2.20
Const相关知识点 • Const int *p: p指向一个 const int 类型变量 • Int * const p: p指向int 类型变量 p本身是一个只读变量
c++中对于const变量初始化为字面量,会有类似于宏定义的优化
• 非常成员函数中:this指针是 T* const this
• 常成员函数中:this指针是 const T* const this
面试题:如何在常成员函数中修改成员变量? 答:将this指针强转成非const指针,用新指针修改成员变量: T* p=(T*)this
constexpr:c++11中引入关键字。修饰的变量为常量
enum枚举类型 c++11中引入了枚举类来解决c语言中枚举的一些问题 1.c语言中的枚举变量 一定是一个4字节的int型(不可改变数据类型) 2.c语言中两个不同枚举在相同作用域下可能冲突
1.enum class HeroState:char{State1,state2,state3} //可以修改底层的类型为一个字节 2.想用枚举值必须加作用域:HeroState::state1
头文件<limits.h>: 里面存储了各种基本类型变量的长度和一些信息,用宏的方式表示。方便可以跨平台的c/c++程序
整形提升规则: //所有表达式和变量计算时,如果计算的操作数小于4个字节,就转换为4个字节来计算。 //char和short,进行计算时,会直接转为int计算 //32位系统,底层的cpu操作数据的基本单位是4个字节,无法单独操作char和short,所以需要转换为int //同类型的有符号和无符号运算,会转换为无符号变量再运算
2021.2.21 原始字符串(Raw String) ——为了解决字符串转义引起的问题 当我们需要一行字符串的时候,字符串转义往往成了一个负担,写和读都带了很大的不便。例如,对于如下路径"D:\workdataDJ\code\vas_pgg_proj",我们必须通过反斜杠进行转义,把它写成如下形式: string path = "D:\workdataDJ\code\vas_pgg_proj";
在C++11中提供了语法可以将字符串内容原原本本的转换成字符串 (转义字符或回车都可以被识别为原本含义) 语法格式如下: (1)字符串前加R前缀; (2)字符串首尾加上小括号; 例如:
C++11原始字符串同时包含其它特点: 1. 字符串中的换行符将在屏幕上如实显示。 2. 在表示字符串开头的"和(之间可以添加其它字符,不过必须在表示字符串结尾的)和"之间添加同样的字符。 第二个特性允许在字符串中使用任何和原始字符串边界标识不同的任意字符组合,而不用担心提前结束原始字符串,比如使用“) .因为默认是要读到)"就结束 怕字符串里也有 错误结束了
自定义字面量后缀 仿照25L是long类型的变量,25.f是float类型的变量。也可以自定义后缀的字面量的功能。例如定义void operator""zz(unsigned long long ){}可以让使用25zz时,自动跳转到你定义的函数。参数可以是(unsigned long long)、(char)、(const char*str,size_t n)等等
可执行体系列: 函数指针 指向了一个函数。你可以保存这个指针,也可以传递这个指针,都可以。 一个函数指针对应一个函数
成员函数指针
函数对象(仿函体) c++中用来代替c语言中的函数指针。作为一个对象,可以存储状态,有各种属性和行为
Lambda表达式 • []{};//最简单的lambda表达式 ↑相当于struct lambda { Void operator()(){} }; • {};//括号表示参数,如果是无参,可以省略 • [](int v){};//括号中是参数 • ->int{return 0;};//可以显式指定返回值类型 • {return 5.5;};//可以自动推导返回值类型,但如果函数体中有多个分支,要注意返回类型不能冲突 • {cout<<"创建时直接调用";}();//可以后边加括号来直接调用lambda表达式 捕获功能: lambda表达式本质是生成了一个函数对象☆。因为是函数对象,所以可以有成员变量,在创建该对象时捕获所有[]中标明的局部变量。
捕获分两种,值捕获和引用捕获. • this,a,&b{};//值捕获变量a,引用捕获变量b.捕获this指针(this指针只能值捕获,引用捕获无意义) • ={};//自动值捕获lambda函数体用到的函数局部变量 • &{};//自动引用捕获lambda函数体中用到的函数局部变量
function可执行体: c++中常用可执行体概念包括函数指针,成员函数指针,仿函体,lambda对象,bind()函数的返回值等。
2021.2.22
bind函数适配器 ——可以将一个参数列表的函数适配成另一种
四种强制转换☆ c语言风格的强制转换功能强大。但它把很多本质区别很大的类型转换用同一种写法表示,所以c++11引入四种更细划分的类型转换代替c语言的类型转换 1. Static_cast静态类型转换:用于在基本类型之间转换以及编译期间确定的指针类型转换,子类指针转换成父类指针安全,父类指针转换成子类指针不一定安全。不同类型指针转换不一定安全。(最常用) 2. Const_cast常量类型转换:用于去掉变量指针的const修饰或者volatile修饰符。 将const指针转换为同类型的非const指针时用它。 3. Dynamic_cast动态类型转换:用于安全的在父子类之间的转换。类必须拥有虚函数(dynamic_cast通过虚指针确定是否安全)。转换引用时,不安全抛bad_cast异常。 4. Reinterpret_cast重新解释转换:不常用,只能用于转换指针。转换类型时不会改变指针的二进制内容。
2021.2.23
Typeid 可以获得一个变量或者一个类型的typeinfo结构的引用。主要用其中的name()方法获得类型名的字符串。 例如:
Bad_typeid异常: 对象有虚指针,但对象是空指针指向的对象,就会抛出该异常
assert断言机制(多用于判断空指针) • 头文件:<assert.h> • 作用:C语言中,用来辅助程序员定位bug的一个工具。 • 使用方式:assert(表达式);当表达式为假的话,就会出错误并退出程序。 例:
可以在包含头文件之前定义一个宏NDEBUG,让所有assert()失效
判断空指针
array容器 在栈中的数组,不可以动态扩容。 和vector的区别:vector的数据全存在堆区,并且可以动态扩容 用法: Array<int,20>arr;
Unordered_set/unordered_map c++11引入了哈希表工具,用来方便程序员进行编程。使用方式和set/map基本一致。只有内部结构不同。 unordered_set/unordered_map中如果想使用自定义类型,需要指定好对应的hash方法和equal_to比较方法。 下列各种使用方法: • 自定义类型,如何使用map/set存放或者用unordered_map/set存放
Extern “c”:以c语言的方式编译
注意如何调用的 以及创建对象的方法
->g类似于一个函数指针的功能 如果第一个大于第二个返回true 否则返回false 重载了()运算符
数字从大到小排列
相当于创建了一个匿名的类
f与f2等价
无法捕获在lambda表达式下面的变量 比如int e=88;再使用 报错
主要就是占位符的理解和运用,下面为几种不同情况
十
->2,1
• 该重载<<运算符函数可以让cout直接输出一个student对象
• 使用外部比较器 来给定排序顺序
//1.通过在模板中指定可用的仿函体的类,让内部可以正常比较大小 • 使用外部比较器法
//2.需要定义student类中的operator<,让less变为有效 • 使用内部比较器法
• 传入指定的模板参数,来告诉容器如何哈希一个student对象,和如何比较两个student对象是否相同
• 用传入内部比较器的思想实现比较两个student是否相同(定义student的operator==函数)
初始化列表Initializer_list(一个模板类) 头文件:<initializer_list> c++11中引入了初始化列表,从而可以让形如{125,32,55,76}的列表作为参数传递到函数中。注:c++98中,无法用vectorarr={123,5,23}的语法来初始化
只要你在函数调用的时候使用{},就会创建一个initializer_list的临时对象,用来构造vector
Initialize_list类成员: 首位迭代器:begin()//第一个元素的迭代器,end()//最后一个元素下一个元素的迭代器 长度:size() 例子:
这是一个参数可变常的print函数
2021.2.24
右值 按名字来理解,c++中,可以放到=左边的值叫左值。比如普通变量和引用, 不能放到=左边,只能放到=右边的值叫右值。比如字面量和一些临时变量, 可以理解为,能获取地址的值,就是左值。不能获取地址的值叫右值。
c++11中,明确了左值和右值的概念,并且将右值的概念分为了两种;左值的概念还是之前的未变。 右值: • 纯右值:c++中定义的右值,字面量,临时变量等没有地址的值 • 将亡值:c++11的某些新语法获得到的一类右值。在几种特殊情况下,把表达式视为右值的情况↓ 1.move函数返回值就是将亡值 //例如:func(move(a)) 返回的值 将亡值 2.如果函数返回值是右值引用。返回值是将亡值 3.将左值强制转换为右值类型,该强制转换的返回值是将亡值
左值引用和右值引用 c++11将变量的引用分成两类引用,左值引用和右值引用 左值引用:引用左值 右值引用:引用右值 右值引用引用的右值,会被系统分配空间,所以右值引用本身就是左值了。
左值引用举例: Int a = 25; Int &ra=a;
Const int ca=66; Const int& ra2=ca; Const int& clr=236; Const NODE& n=NODE(); //常量左值引用,既可以引用左值也可以引用右值
//右值引用,只能引用右值 Int a=77; Int&&ra2=5; func(ra2) //右值引用本身,是一个左值 &r2;//可以去地址
NODE& n=NODE(); Const NODE&& n1=NODE(); Const NODE&& n2=(const NODE( ))
看上图 分别调用: Func(get1())匹配到右值 Func(get2())匹配到左值 Func(get3())匹配到右值 因为函数返回值是右值引用 对应第二种情况
2021.2.25
swap函数和移动语义 swap本身可以借助深拷贝技术实现(如果使用浅拷贝,容易出重复释放问题)。但深拷贝会导致堆区资源在swap中深拷贝三次,浪费性能和资源。可以借助c++的移动语义来优化swap(移动语义包括移动构造和移动赋值) 模板:
swap和移动构造代码:
引用折叠 C++11中模板中的模板参数和形参的类型如果都出现了引用,就可以根据指定规则折叠成左值引用还是右值引用。
指定规则 如果出现一个左值引用,则最终类型就是左值引用 否则,当出现至少一个右值引用时,最终类型就是右值引用 实现的效果:可以形参处定义为TR&&v,来让外界传入左值,最终类型就是左值引用,外界传入右值,最终类型就是右值引用
完美转发☆(important) 想在一个函数中对参数出来完后,将参数传给其他函数,还能保留该参数的左右值属性 模板:
面向对象更新
C++11引入了一些语法和关键字
• 类中成员初始化
给成员设置一个默认值,如果该成员在某个构造函数的初始化参数列表被初始化。如果调用该构造函数创建对象,就是用初始化参数列表中的初始值。否则是用设定的默认值
• DEFAULT关键字
——显式缺省(告知编译器生成函数默认的缺省版本)
可以用default指示系统生成指定的默认函数。增强可读性
• delete关键字
——显式删除(告知编译器不生成函数默认的缺省版本)
用于删除函数。可以让指定的默认函数不再生成
• OVERRIDE关键字
检查指定函数是否重写了父类的函数,如果没有重写父类函数就会报错
• FINAL关键字
两个用途
1.让类不可以被继承
2.让函数不可以被重写
2021.2.27
前言:
如果在初始化参数列表中赋值,则之前的设定的默认值不会被使用 POD类型 POD(plain old data)类型,和C语言兼容的类型。如果A是POD类型,就可以用A a{成员1,成员2…}的方法将其初始化。并且可以安全的使用malloc和其他c语言函数进行操作。
c++11中放宽了对POD类型的约束,基本上只要一个类是默认构造和默认析构,没有虚指针(没有虚函数,也没用虚继承),其成员也都是POD类型,就认为该类是POD类型。
可以在#include<type_traits>中的is_pod来判断是否是POD类型
举例:
统一的初始化方法 C++11让c++对象也可以用{}来初始化,来和pod类型风格统一初始化的写法,增强代码可读性。
增强的sizeof 在c++11中,可以直接用sizeof(A::var)的方式获得非静态成员的大小
委托构造 ——在一个构造函数中,委托初始化的任务给另一个构造函数 简单说,一个构造函数中,调用了另一个构造函数
限制: 1. 如果一个构造函数a,委托了另一个构造函数b,则a的初始化参数列表中不能再设置其他初始值。 2. 不可以循环委托 优点: 可以将详细的构造的任务放在一个构造函数中。让其他的构造函数委托给该函数,防止代码重复 例:
main函数: Student S(5); 输出顺序:
因为传age时调用了委托构造age score的函数再委托name score age的函数
继承构造 正常情况下,构造方法不会被继承。如果子类只是想用父类构造的参数,子类本身有功能需要加入,也需要写大量的无意义的代码。c++11引入了继承构造机制,可以方便的"继承"到所有父类构造。 核心代码 Using 父类::父类; 旧方法
C++11中新方法
模板工作原理 Template void run(){T a;/…/}; //一个函数模板 Templateclass A{/…/}//一个类模板 可以定义函数模板或类模板,编译器在编译时,在第一次使用了某个函数模板(类模板)并定义好了所有模板函数,就会生成一份对应的模板函数(模板类),如果之前当前源文件已经生成功过对应模板参数的模板函数(模板类),就使用之前生成的 例:
2021.02.28
c++11中提供的模板机制
外部模板 • 在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化 。 这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化 。而且没有办法告诉C++不要引发模板的实例化。 C++11引入外部模板这一概念。
• 指明不需要再本文件中生成对应参数的模板函数,该函数在其他文件中就会生成。如果该函数没在其他文件中生成,使用时会报找不到外部符号…错误。
• 编译器要对每一处模板进行实例化,链接器还要移除重复的实例化代码。当在广泛使用模板的项目中,编译器会产生大量的冗余代码,这会极大的增加编译时间和链接时间。
解决这个问题的方法和全局变量的一样,就是使用外部模板。 外部模板依赖c++98中已有特性,显式实例化
使编译器在编译单元中实例化出一个fun(int)版本的函数 例:
这样在test2.o中就不再生成fun(int)实例化代码
编译器不用每次实例化都产生一份 fun(int)的代码,减少编译时间。链接器的工作也很轻松, test1.cpp和test2.cpp共享一份代码。
静态断言
可以用来在编译期间判断模板等某条件是否成立。如果不成立,会提示第二个参数指明的文本。比如模板参数,类型长度等编译器确定的信息。 模板参数包 语法: 用来定义可变长的模板。 Template<typename …args>void run(args …a){} 没有办法直接获得模板参数包中的某一个参数。
获得模板参数包参数数量: Sizeof…(args)或sizeof…(a)可以获得指定模板参数包中模板参数的个数
递归拆包 想要获得参数值只能递归拆包,用template<typename T,typename …a>的模板可以拆出一个参数,详情见代码。 递归拆包会产生大量中间代码。但是不影响开发效率。 递归拆包需要一个递归拆包终止函数。用于只剩最后一个参数的输出。 例:
自动推导返回值类型 返回值类型后置: c++11允许一种新的定义返回值类型的方法, Auto run()->void{} 该函数返回值被定义为void. Decltype 获取变量类型 可以用decltype获得某个变量类型。 Int a=35;decltype(a)b=46; decltype(a+35.5) c=77.56; 模板中自动推导返回值类型 在模板中,如果有多个不同类型模板,根据不同模板参数会得到不同的返回值类型,此时可以使用 自动推导返回值类型 例:
练习代码:
四种智能指针 https://www.zhihu.com/search?type=content&q=%E5%9B%9B%E7%A7%8D%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88 (详细解释) 前言: 为什么要使用智能指针? 智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
Auto_ptr Auto_ptr是最老版本智能指针,因为其缺陷,现在已经不再使用。C++98/03引入。不涉及任何C++11知识就可以理解。其"拷贝构造"其实际效果是移动了内部堆区对象的管理权。
Unique_ptr(用来替换auto_ptr)
unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。unique_ptr 是一种定义在头文件<memory>中的智能指针。它持有对对象的独有权——两个unique_ptr不能指向一个对象,即 unique_ptr 不共享它所管理的对象。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL)算法。只能移动 unique_ptr,即对资源管理权限可以实现转移。这意味着,内存资源所有权可以转移到另一个unique_ptr,并且原始 unique_ptr 不再拥有此资源。实际使用中,建议将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于存 C++ 对象时,可使用 unique_ptr,构造 unique_ptr 时,可使用 make_unique Helper 函数。
一个智能指针对象唯一对应一个堆区资源 代码:
Unique_ptr删除器 unique_ptr中默认的释放资源的方式是用delete,但并不是所有资源都应用delete释放,也可能需要delete[]或free来释放或其他方式。所以可以指定删除方式
Shared_ptr Shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的) 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在 memory 文件中,命名空间为 std。shared_ptr最初实现于Boost库中,后由 C++11 引入到 C++ STL。
Shared_ptr 利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个 shared_ptr 共同管理同一个对象。像 shared_ptr 这种智能指针,《Effective C++》称“引用计数型智能指针”(reference-counting smart pointer,RCSP)
多对一的智能指针。内部使用引用计数机制,当最后一个shared_ptr也不指向资源的时候,就释放资源
Shared_ptr删除器 和unique_ptr同理,资源可能不应该用delete释放。所以可以指定释放的方式,通过传入一个可执行体来指示应该如何删除
循环引用问题
比如双向链表中用智能指针代替普通指针,可能出现循环引用而导致计数始终不能为0,导致内存泄漏
Weak_ptr weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
专门用于解决shared_ptr的循环引用问题。资源的释放不会在意是否被weak_ptr管理。想用weak_ptr指向的资源,只能尝试性的使用。如果此时资源存在可以使用,如果此时资源不存在,就无法使用
解决循环引用问题
使用weak_ptr代替双向链表节点中的shared_ptr.来打破循环引用
资源可能失效 weak_ptr对象使用lock函数获得对应资源的shared_ptr,如果资源已经失效,则获得一个指向空的shared_ptr