diff --git a/C++/C++基础/2 字符串、向量和数组.md b/C++/C++基础/2 字符串、向量和数组.md index e9c62821..3de444e5 100644 --- a/C++/C++基础/2 字符串、向量和数组.md +++ b/C++/C++基础/2 字符串、向量和数组.md @@ -155,12 +155,24 @@ char a3[]="c++"//自动添加表示字符串结束的空字符 ``` //下标遍历 - + int m[5]={1,2,3,4,5}; + for(int i=0;i<5;i++){ + cout<(p)//然后正常使用dp。也可以转换为其他类型,取前一个字节或者四个字节。 +``` + +- const_cast 去掉底层的 const +- reinterpret_cast 为运算对象的位模式提供较低层次上的重新解释。 + +### C 强制类型转换 + +``` +type(expr); +(type)expr; +``` + +## 12 优先级 + +![](2021-03-04-20-43-17.png) + +![](2021-03-04-20-43-53.png) diff --git a/C++/C++基础/3.cpp b/C++/C++基础/3.cpp new file mode 100644 index 00000000..f0d038d0 --- /dev/null +++ b/C++/C++基础/3.cpp @@ -0,0 +1,10 @@ +#include + +using namespace std; + +int main(){ + int a=1; + int b=2; + int c=(b<<1); + cout<<(a|b)+c< process() +{ +return {"function","hello"} +} + +> 原来 C++11 中添加大括号进行初始化的主要目的,就是返回多个值啊。但是返回值类型还是必须相同的。也就是说可以使用中括号进行复制列表初始化。 + +### 返回数组指针 + +## 4 函数重载 + +### 定义重载函数 + +- 如果同一个作用域的几个函数名字相同但是形参列表不同,则成为函数重载。 +- 可以通过 const 来区分重载了哪个具体的函数。 + +### 调用重载函数 + +- 函数匹配 +- 重载确定 + +### 重载与作用域 + +- 函数外部的重载函数可能被局部同名变量屏蔽掉。 + +## 5 特殊用途语言特性 + +### 默认参数 + +- 实参按位置解析,默认参数填补缺少的尾部实参。 + +### 内联函数 + +- 普通的函数调用操作系统需要开单独的空间,保护寄存器状态。 +- 内联函数相当于常量,可以直接替换使用到内联函数的地方。直接嵌入到函数调用的地方。 + +### constexpr 函数 + +- 用于常量表达式的函数。 +- 函数的返回值及所有的形参的类型都是字面值常量 +- 函数中只有一条 return 语句。 + +- 保证函数在编译过程中能够直接计算结果。 + +### assert 预处理宏 + +``` +assert(expr); +``` + +- 预处理变量,使用表达式作为条件。 +- 程序执行过程中,如果表达式为真,则 assert 什么也不做。如果表达式为假,则输出信息终止程序。 + +## 6 函数匹配 + +1. 选定本次调用对应的重载函数集,集合中的函数称为候选函数。候选函数的特点:与被调用的函数重名;声明在调用点可见。 +2. 选出能被实参调用的函数,称为可行函数。实参与形参的数量相等;实参与形参对应的类型相同。 +3. 默认参数的重载函数优先级,高于,类型转换的重载函数的优先级。 + +![](2021-03-04-22-45-04.png) + +## 7 函数指针 + +### 定义函数指针 + +- 函数指针指向的是函数,而非变量或对象。函数指针指向某种特定的类型的函数。函数的类型由它的返回值类型和形参类型共同决定,与函数名无关。 + +```C++ +bool lengthCompare(const string &,const string &); +bool (*pf)(const string &,const string &); +//*pf两端如果没有括号,则指针是指向返回值的指针。 +//两个函数是等价的 +``` + +### 使用函数指针 + +- 当把函数名作为一个值使用时。函数自动的转换成指针。 + +``` +pf = lengthCompare; +pf = &lengthCompare;//两者是等价的。 +pf = 0;//pf不指向任何函数 +pf = nullptr;//pf不指向任何函数。 +``` + +- 在多个重载函数中,pf 的类型必须指向特定的某个重载函数。 diff --git a/C++/C++基础/6.cpp b/C++/C++基础/6.cpp new file mode 100644 index 00000000..6b7a8b86 --- /dev/null +++ b/C++/C++基础/6.cpp @@ -0,0 +1,32 @@ +#include +#include +using namespace std; + +int hello(int a, int b) +{ + cout << a << b << endl; + return 0; +} + +//多个形参列表,必须是相同类型。 +//另外把这个东西换成vector也是没有问题的。 +//但是这个东西C++默认提供的类型,不需要头文件。 +int multi_value(initializer_list lst) +{ + auto begin = lst.begin(); + auto last = lst.end(); + while (begin != last) + { + cout << *begin << endl; + begin++; + } + return 0; +} + +int main() +{ + int i = 0; + // hello(i+1,i=i+1); + multi_value({1, 2, 3, 5}); + return 0; +} \ No newline at end of file diff --git a/C++/C++基础/7 类.md b/C++/C++基础/7 类.md new file mode 100644 index 00000000..f754afeb --- /dev/null +++ b/C++/C++基础/7 类.md @@ -0,0 +1,178 @@ +# 类 + +## 1 定义抽象数据类型 + +### 概念 + +- 数据抽象是一种依赖于接口和实现的分离的编程技术。 +- 类的接口包括用户所能执行的操作;类的实现包括类的数据成员、负责接口实现的函数体以及定义类所需要的的各种私有函数。 +- 封装实现了类的接口和实现的分离。 + +### 设计类 + +- 定义包含数据成员和成员函数 +- 定义在类内部的函数是隐式的 inline 函数! + +### this 指针 + +- this 指针。用来指明当前绑定的对象。只在依赖对象的函数中添加 this 指针。 + +### 常量成员函数 + +- 常量成员函数。const 关键字放在成员函数的参数列表之后。常量函数的 this 指针指向常量对象。不能对常量对象内数据进行修改。 + +``` +class A{ + const int getM(){//表示返回值是const类型的 + + } + + int getN() const{//表示函数是const类型的 + + } +} +``` + +- 当一个函数被 const 说明后,其函数中出现的对外部数据的任何写入或修改都将被系统检查为出错。如果把函数 writeme()说明为: + +``` +void writeme(int i) const { me = i; } //系统将会检查出错误。 +``` + +- 作用:当函数体较大且复杂时,如果我们希望系统帮助避免对对象内容进行修改,那么我们就会将这个函数定义为常量型函数,这就是使用它的主要目的。 + +### 构造函数 + +- 控制对象的初始化过程。只要类对象被创建就会执行构造函数。 +- 构造函数没有返回类型。构造函数不能被声明成 const +- 默认构造函数没有任何实参。 +- **合成的默认构造函数**。编译器隐式地创建的默认构造函数。合成默认构造函数初始化过程: + - 如果类内存在初始值,使用它来初始化成员 + - 否则,默认初始化该成员。 + +``` +hello{ + hello()=default;//要求编译器生成默认的构造函数。 +} +``` + +### 构造函数的初始值列表 + +``` +hello{ + heelo(int aa,int bb):a(aa),b(bb),c(0){ + + };//列表初始化。 +} +``` + +## 2 访问控制与封装 + +### 访问说明符 + +- public,对外部函数可见。 +- private,被类成员访问,但不能被外部访问。private 部分封装了类的实现细节。 + +### 定义类 + +- class 和 struct 都能定义类。 +- class 的默认访问控制是 private +- struct 的默认访问控制是 public + +### 友元 + +- 类的接口可以不是类的成员。下图中,print、add、read 是类的接口但是不是类的成员。被称作非成员接口函数。 + +![](2021-03-05-11-26-54.png) + +- 非成员接口函数无法访问类的私有变量。可以声明友元,访问私有变量。友元的声明智能出现在类定义的内部。友元是突破访问控制符的方法。 + +![](2021-03-05-11-28-27.png) + +- 友元也为多种形式的接口提供了方法。比如可以重载上述的 read 函数,让它成为多个不同的类友元。可以实现一个接口在多种不同情况下的使用。 + +## 3 类的其他特性 + +- 类内的函数可以被声明为内联函数 +- 成员函数可以被重载。 +- (日了狗了,真 TM 特例也太多了吧,有必要吗)可变成员函数,mutable 声明的变量,是可变数据成员。即是是 const 对象的成员,它也是可变数据成员,依然能够改变。 + +### 类类型 + +- 类通过类名来标识。如果两个类的内容完全一致,但是类名不一样,也无法相互赋值。 +- 两种创建对象的方法 + +``` +Hello h; +class Hello h;//包含关键字class创建对象。两者等价。 +``` + +- 类可以进行前向声明,而不进行定义。他是一个不完全的类型。 + +``` +class Hello; +``` + +- 在创建类的对象之前,类必须被定义。所以如果要引用其他头文件中的类,类必须在该头文件就被定义,否则没办法创建对象进行连接。 + +- 其他类、类成员都可以声明为类的友元。 + + +## 4 类的作用域 + +- 在类的作用域外,通过对象、引用、指针,使用成员访问运算符访问。 +- 在类外定义函数,需要指明类作用域,使用作用域运算符::实现类内的函数。函数体内可以直接使用类作用域内的内容。函数体外的返回值通过作用域运算符使用类作用域内的内容。 + +### 名字查找与类的作用域 + +名字查找的步骤 +* 作用域内查找使用之前的声明。 +* 查找外层作用域的声明 +* 如果没找到报错。 + +类内名字查找比较特殊。 +* 首先编译成员声明。 +* 然后编译函数体。 +* 所以即使类的成员声明在类的末尾,对之前的函数来说也是可见的。 + +成员定义中普通块作用域的名字查找 + +* 首先在成员函数内查找声明。 +* 类内查找。所有的类成员都被考虑。 +* 在成员函数定义之前的作用域内查找。 + +## 5 构造函数再探 + +* 当成员是常量或引用的时候,初始化是必不可少的。 +* 成员初始化的书序与他们在类定义中的出现顺序一致。而非初始化列表中传入参数的顺序。 + +### 委托构造函数 +* 委托构造函数使用类的其他构造函数执行自己的初始化过程。 + +![](2021-03-05-13-42-02.png) + +初始化执行的新婚徐 + +1. 初始化列表 +2. 委托构造函数 +3. 初始化函数体 +4. 值初始化。(在成员变量定义的时候给出的值) +5. 默认初始化(以上情况都没有的时候) + +### 默认构造函数的作用 + + +默认初始化在一下情况发生: +* 块作用域内不适用任何初始值定义一个非静态变量 +* 一个类本身含有类类型的成员其而是用合成的默认构造函数时。(包含关系的外部合成默认构造函数会自动调用内部的默认构造函数。)如果有自定义的默认构造函数,需要自己手动初始化内部默认构造函数。 +* 类类型的成员没有在构造函数的处置类表中显式的初始化。 + +值初始化在一下情况发生 + + + +> 类的基本特性 +> +> - 数据抽象和封装 +> - 继承 +> - 多态 diff --git a/C++/C++基础/7.cpp b/C++/C++基础/7.cpp new file mode 100644 index 00000000..52753b29 --- /dev/null +++ b/C++/C++基础/7.cpp @@ -0,0 +1,74 @@ +#include +#include +using namespace std; + +// 这种使用流的方式感觉很好玩。 +class Book{ +private: + int a; + int b; +public: + istream &read(istream &is) + { + is>>a>>b; + return is; + } + + ostream & print(ostream &os) + { + os< vc(3);//vector会自动调用元素对象的默认初始化函数。 + // cout< ttt{Car(3.4),Car(5,6)}; + cout< (expression) +dynamic_cast (expression) +const_cast (expression) +reinterpret_cast (expression) +``` + +- static_cast +- dynamic_cast +- const_cast +- reinterpret_cast + +## 2 详解 + +### static_cast + +static_cast 相当于传统的 C 语言里的强制转换,该运算符把 expression 转换为 new_type 类型,用来强迫隐式转换,例如 non-const 对象转为 const 对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法: + +- 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。 + - 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的; + - 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。 +- 用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成 enum。这种转换的安全性也要开发人员来保证。 +- 把空指针转换成目标类型的空指针。 +- 把任何类型的表达式转换成 void 类型。 + +> 注意:static_cast 不能转换掉 expression 的 const、volatile、或者\_\_unaligned 属性。 + +1. 基本类型数据转换举例如下: + +``` +char a = 'a'; +int b = static_cast(a);//正确,将char型数据转换成int型数据 + +double *c = new double; +void *d = static_cast(c);//正确,将double指针转换成void指针 + +int e = 10; +const int f = static_cast(e);//正确,将int型数据转换成const int型数据 + +const int g = 20; +int *h = static_cast(&g);//编译错误,static_cast不能转换掉g的const属性 +``` + +2. 类上行和下行转换: + +``` +if(Derived *dp = static_cast(bp)){//下行转换是不安全的 + //使用dp指向的Derived对象 +} +else{ + //使用bp指向的Base对象 +} + +if(Base*bp = static_cast(dp)){//上行转换是安全的 + //使用bp指向的Derived对象 +} +else{ + //使用dp指向的Base对象 +} +``` + +### dynamic_cast + +- dynamic_cast 主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换(cross cast)。 + - 在类层次间进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的; + - 在进行下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。 +- dynamic_cast 是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。 + +``` +dynamic_cast(e) +dynamic_cast(e) +dynamic_cast(e) +``` + +- type 必须是一个类类型 + - 在第一种形式中,type 必须是一个有效的指针 + - 在第二种形式中,type 必须是一个左值 + - 在第三种形式中,type 必须是一个右值。 +- 在上面所有形式中,e 的类型必须符合以下三个条件中的任何一个: + - e 的类型是是目标类型 type 的公有派生类 + - e 的类型是目标 type 的共有基类 + - e 的类型就是目标 type 的的类型。 +- 如果一条 dynamic_cast 语句的转换目标是指针类型并且失败了,则结果为 0。如果转换目标是引用类型并且失败了,则 dynamic_cast 运算符将抛出一个 std::bad_cast 异常(该异常定义在 typeinfo 标准库头文件中)。e 也可以是一个空指针,结果是所需类型的空指针。 + +1. 指针类型. + 举例,Base 为包含至少一个虚函数的基类,Derived 是 Base 的共有派生类,如果有一个指向 Base 的指针 bp,我们可以在运行时将它转换成指向 Derived 的指针,代码如下: + +``` +if(Derived _dp = dynamic_cast(bp)){ +//使用 dp 指向的 Derived 对象 +} +else{ +//使用 bp 指向的 Base 对象 +} +``` + +值得注意的是,在上述代码中,if 语句中定义了 dp,这样做的好处是可以在一个操作中同时完成类型转换和条件检查两项任务。 + +2. 引用类型 + +因为不存在所谓空引用,所以引用类型的 dynamic_cast 转换与指针类型不同,在引用转换失败时,会抛出 std::bad_cast 异常,该异常定义在头文件 typeinfo 中。 + +``` +void f(const Base &b){ +try{ +const Derived &d = dynamic_cast(b); + //使用 b 引用的 Derived 对象 +} +catch(std::bad_cast){ +//处理类型转换失败的情况 +} +} +``` + +### const_cast + +- const_cast,用于修改类型的 const 或 volatile 属性。 + +- 该运算符用来修改类型的 const(唯一有此能力的 C++-style 转型操作符)或 volatile 属性。除了 const 或 volatile 修饰之外, new_type 和 expression 的类型是一样的。 + - 常量指针被转化成非常量的指针,并且仍然指向原来的对象; + - 常量引用被转换成非常量的引用,并且仍然指向原来的对象; + - const_cast 一般用于修改底指针。如 const char \*p 形式。 + +举例转换如下 + +``` +const int g = 20; +int *h = const_cast(&g);//去掉const常量const属性 + +const int g = 20; +int &h = const_cast(g);//去掉const引用const属性 + +const char *g = "hello"; +char *h = const_cast(g);//去掉const指针const属性 +``` + +### reinterpret_cast + +- new_type 必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。 + +- reinterpret_cast 意图执行低级转型,实际动作(及结果)可能取决于编辑器,这也就表示它不可移植。 + +- 举一个错误使用 reintepret_cast 例子,将整数类型转换成函数指针后,vc++在执行过程中会报"...中的 0xxxxxxxxx 处有未经处理的异常: 0xC0000005: Access violation"错误: + +``` +#include +using namespace std; +int output(int p){ + cout << p <(&p); + fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation + return 0; +} +``` + +## 3 c++强制转换注意事项 + +- 新式转换较旧式转换更受欢迎。原因有二,一是新式转型较易辨别,能简化“找出类型系统在哪个地方被破坏”的过程;二是各转型动作的目标愈窄化,编译器愈能诊断出错误的运用。 +- 尽量少使用转型操作,尤其是 dynamic_cast,耗时较高,会导致性能的下降,尽量使用其他方法替代。 diff --git a/C++/面试/11.初始化的方法.md b/C++/面试/11.初始化的方法.md new file mode 100644 index 00000000..09c58724 --- /dev/null +++ b/C++/面试/11.初始化的方法.md @@ -0,0 +1,118 @@ +# 初始化方法 +> 参考文献 +> * [谈谈C++中各种初始化方式](https://blog.csdn.net/u014359097/article/details/50788911) +> * [C++的各种初始化方式](https://www.cnblogs.com/pluse/p/7088880.html) +## 1 初始化方法 + +C++中的初始化主要包括五种: +* 默认初始化 +* 值初始化 +* 直接初始化 +* 拷贝初始化 +* 列表初始化 + + +``` +class Car{ +public: + int a=0; + int b=1; + + Car(int aa,int bb):a(aa),b(bb){};//初始化列表,进行初始化 + Car(int aa):Car(aa,0){};//委托构造函数,使用第一个构造函数进行构造。 + Car():Car(0,0){};//默认构造函数,委托构造函数 + string m="fjeioaf"; + string *n=&m; +}; + +//一下是初始化方法 + Car t1(3,4);//调用构造函数 + Car t2 = Car(3,4);//调用构造函数 + // Car* t = new Car(3,4); + Car tt{3,4};//可以调用构造函数 + vector ttt{Car(3.4),Car(5,6)}; +``` +### 默认初始化 + +默认初始化是定义对象时,没有使用初始化器,也即没有做任何初始化说明时的行为。 + +``` +int i; +vector v; +``` +原理: + +这些变量被定义了而不是仅仅被声明(因为没有extern关键字修饰),而且没有显式的赋予初值。特别的,如果采用动态分配内存的方式(即采用new关键字)创建的变量,不加括号时(如int *p=new int;)也是默认初始化,加了括号(如int *p=new int())为值初始化。变量的值与变量的类型与定义的位置有关系。 + +1. 对于内置类型变量(如int,double,bool等),如果定义在语句块外(即{}外),则变量被默认初始化为0;如果定义在语句块内(即{}内),变量将拥有未定义的值。 + +2. 对于类类型的变量(如string或其他自定义类型),不管定义于何处,都会执行默认构造函数。如果该类没有默认构造函数,则会引发错误。因此,建议为每个类都定义一个默认构造函数(=default)。 + +### 值初始化 +> 未明确的直接初始化 +* 值初始化是值使用了初始化器(即使用了圆括号或花括号)但却没有提供初始值的情况。 + +``` +int i{}; +new int(); +new int{}; +``` +原理: +1. 当不采用动态分配内存的方式(即不采用new运算符)时,写成int a();是错误的值初始化方式,因为这种方式声明了一个函数而不是进行值初始化。 +2. 如果一定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int(); +3. 值初始化和默认初始化一样,对于内置类型初始化为0,对于类类型则调用其默认构造函数,如果没有默认构造函数,则不能进行初始化。 + + +### 直接初始化和拷贝初始化 +> 从形式而言,相对 +* 直接初始化与拷贝初始化对应,其内部实现机理不同。 + * 直接初始化是指采用小括号的方式进行变量初始化(小括号里一定要有初始值,如果没提供初始值,那就是值初始化了!)。 + * 拷贝初始化是指采用等号(=)进行初始化的方式。拷贝初始化看起来像是给变量赋值,实际上是执行了初始化操作,与先定义再赋值本质不同。 + +```C++ +vector v1(10); //直接初始化,匹配某一构造函数 +vector v2(10); //直接初始化,匹配某一构造函数 +vector v3=v1; //拷贝初始化,使用=进行初始化 +``` +原理: +1. 对于内置类型变量(如int,double,bool等),直接初始化与拷贝初始化差别可以忽略不计。 + +2. 对于类类型的变量(如string或其他自定义类型),直接初始化调用类的构造函数(调用参数类型最佳匹配的那个),拷贝初始化调用类的拷贝构造函数。 + +### 列表初始化 +列表初始化是C++ 11 新引进的初始化方式,它采用一对花括号(即{})进行初始化操作。能用直接初始化和拷贝初始化的地方都能用列表初始化,而且列表初始化能对容器进行方便的初始化,因此在新的C++标准中,推荐使用列表初始化的方式进行初始化。 +``` +[new] T [object] { arg1, arg2, ... }; +``` + +* 如果T是aggregate集合类型,list对object成员逐个初始化。 +* 如果T不是aggregate类型,编译器查找最匹配的list参数的T的构造函数。 +举例: +``` +int a{3}; +vector b{3,4,5}; +Car t{3,4}; +vector ttt{Car(3.4),Car(5,6)}; +``` +* 所有其他初始化形式都是list initialization的特殊表现形式或者与其相关。理解的要点在于,list中的参数要么按构造函数的参数声明顺序,要么按aggregate类型成员声明顺序,逐个赋值。 + + + + +## 2 类的构造函数 + +### 默认构造函数 +### 合成默认构造函数 +### 委托构造函数 +### 拷贝构造函数 + + +### 构造函数类型和初始化的方法 +> 介绍初始化的方法、构造函数的类型、初始化执行的顺序。 + + +1. 列表初始化 +2. 委托构造函数 +3. 初始化函数体 +4. 值初始化。(在成员变量定义的时候给出的值) +5. 默认初始化(以上情况都没有的时候) \ No newline at end of file diff --git a/C++/面试/8.标准库-头文件md b/C++/面试/8.标准库-头文件md index 55ec98e0..fa894cf1 100644 --- a/C++/面试/8.标准库-头文件md +++ b/C++/面试/8.标准库-头文件md @@ -1,3 +1,8 @@ +# 标准库-头文件 + +* [参考文献](https://blog.csdn.net/sxhelijian/article/details/7552499) + + ## 1 C 标准库 其中包含的引用头文件如下: diff --git a/C++/面试/a.cpp b/C++/面试/a.cpp index 26713921..3d82b073 100644 --- a/C++/面试/a.cpp +++ b/C++/面试/a.cpp @@ -7,4 +7,7 @@ int aa=3; int hello(){ cout<