C++复习(仅供期末复习用)
本文档仅供复习使用,主要目的在于知识点的细化和部分知识点的系统归纳,并不用于从零开始开天辟地(= ▽ =)
垃圾author:@Nakiri洛
参考文档:https://www.runoob.com/cplusplus/cpp-tutorial.html
👆这个文档整理的还是蛮全面的,本文仅是做了部分的摘抄和随笔,用来细化知识和强化记忆
基本内置类型
类型 | 关键字 |
---|---|
布尔型 | bool |
字符型 | char |
整型 | int |
浮点型 | float |
双浮点型 | double |
无类型 | void |
宽字符型 | wchar_t |
对于wchar_t类型,其实是short int类型
枚举类型(enumeration)
是一种派生数据类型,是由用户定义的若干枚举常量的集合。
关键字:
enum
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
……
标识符[=整型常数]
}枚举变量;
//举个例子
enum color{
red,
green,
blue
}c;
c = blue;
/**
默认情况下,第一个名称的值为0,第二个名称的值为1,以此类推。但是如果给其中一个名称赋了一个特殊值,那么后面的名称将会依次比这个赋了特殊值的名称的值大1,但是前面的名称的值没有什么影响。
下面举个例子
*/
enum color{
red, //这里red的值是0
green = 3,
blue //这里bule的值是4
}c;
初始化局部变量和全局变量
当局部变量被定义时,系统不会对其进行初始化,所以局部变量必须自行初始化。定义全局变量时,系统会自动进行初始化,默认值如下表:
数据类型 | 默认初始化值 |
---|---|
int | 0 |
char | ‘\0’ |
float | 0 |
double | 0 |
pointer | NULL |
整数常量
整数常量可以是十进制、八进制、十六进制常量。前缀指定基数:0X或者0x表示十六进制,0表示八进制,不带前缀默认十进制。
整数常量可以带一个后缀,是U和L的组合。U表示无符号整数,L表示长整数。后缀可以是大写,也可以是小写,U和L的顺序随意。
浮点常量
浮点常量由整数部分、小数点部分和指数部分三部分。
当使用小数形式表示时,必须包含整数部分、小数部分,或者同时包含两者。当使用指数形式表示时,必须包含小数点、指数,或者同时包含两者。带符号的指数是用e或者E引入的。
字符常量
如果常量以L(仅当大写时)开头,则表示它是一个***宽字符常量***(比如L‘x’),此时它只能储存在***wchar_t***类型的变量中。否则,它就只是一个窄字符常量。
定义常量
定义常量通常由两种简单的方式
-
使用 #define 预处理器
#define identifier value
-
使用 const 关键字
const type variable = value;
修饰符类型
C++允许在 char、int、double 数据类型前防止修饰符,用于改变基本类型的含义。
数据类型修饰符有:
- signed
- unsigned
- long
- short
可用于整型: 四种均可
**可用于字符型: ** signed、unsigned
**可用于双精度型: **long
修饰符 signed 、unsigned 可以作为 long、short 修饰符的前缀。
如果只写修饰符,默认隐含的是 int 类型
类型限定符
限定符 | 含义 |
---|---|
const | 在程序执行期间不能被修改改变 |
volatile | 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一边的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率 |
restrict | 由restrict修饰的指针是唯一一种访问它所指向的对象的方式。属于C99新增类型限定符 |
储存类
定义变量/函数的范围(可见性)和生命周期。放在他们所修饰的类型之前。
- auto
- register
- static
- extern
- mutable
- thread_local
从C++17开始,auto关键字不再是C++储存类说明符,且register关键字被弃用
auto:
- 声明变量时根据初始化表达式自动推断该变量的类型
- 声明函数时函数返回值的占位符
register(已弃用):
储存在寄存器中而不是RAM种的局部变量。
这就意味着变量的最大尺寸等于寄存器的大小,且不能对他应用一元的 ‘&’ 运算符(因为它没有内存位置)。
static:
在程序的声明周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。故使用static修饰局部变量可以在函数调用之间保持局部变量的值。
当修饰全局变量时,变量的作用域限制在声明它的文件内。
用在类数据成员时,会导致仅有一个该成员的副本被类的所有对象共享。
extern:
提供一个全局变量的引用,全局变量对所有程序文件都是可见的。对于无法初始化的变量,会把变量名指向一个之前定义过的储存位置。
extern是用来在另一个文件中声明一个全局变量或函数。
mutable:
仅适用于类的对象。
thread_local:
所修饰的变量尽可以在它在其上创建的线程上访问。变量在创建线程时创建,并在销毁线程时销毁。每个线程都有自己的变量副本。
该说明符可以与static或者extern合并。、
该说明符仅应用于数据的声明和定义,不能用于函数。
位运算符
以二进制形式表示,逐位进行操作。
真值表:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与操作,按二进制位进行"与"运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; | (A & B) 将得到 12,即为 0000 1100 |
| | 按位或运算符,按二进制位进行"或"运算。运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1; | (A | B) 将得到 61,即为 0011 1101 |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 取反运算符,按二进制位进行"取反"运算。运算规则:~1=-2; ~0=1; | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A >> 2 将得到 15,即为 0000 1111 |
函数参数
形参的传递方式:
调用类型 | 描述 |
---|---|
传值调用 | 该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 |
指针调用 | 该方法把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
引用调用 | 该方法把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
Lambda函数(表达式)
把函数看作对象。Lambda表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
本质与函数声明十分类似。具体表达形式如下:
[capture](parameter)->return_type{body}
//举个例子
[](int x, int y){return x < y;}
//如果没有返回值可以表示为
[capture](parameters){body}
//举个例子
[]{ ++global_x; }
//举个更复杂的例子
[](int x,int y)->{int z = x + y; return z + x;}
Lambda表达式可以访问当前作用域的变量,这是Lambda表达式的 闭包 行为。C++变量传递有传值和传引用的区别,可以通过前面的[]来指定:
形式 | 意义 |
---|---|
[] | 没有定义任何变量,使用未定义变量引发错误 |
[x, &y] | x以传值的方式传入(默认),y以传引用的方式传入 |
[&] | 任何被使用到的外部变量都隐式地以传引用方式加以引用 |
[=] | 任何被使用到的外部变量都隐式地以传值方式加以引用 |
[&, x] | x显式地以传值方式加以引用,其余地以引用方式加以引用 |
[=, &z] | z显式地以引用方式加以引用,其余变量以传值方式加以引用 |
对于[=]和[&]的形式,lambda表达式可以直接使用this指针,但是对于[]形式,如果使用this指针,就必须要用以下方式显式传入:
[this]() { this->someFunc(); };
这个地方就可以理解为[]里面规定了Lambda函数应用当前作用域内变量的方式
数学运算
需要引用数字头文件
函数 & 描述 |
---|
double cos(double); 该函数返回弧度角(double 型)的余弦。 |
double sin(double); 该函数返回弧度角(double 型)的正弦。 |
double tan(double); 该函数返回弧度角(double 型)的正切。 |
double log(double); 该函数返回参数的自然对数。 |
double pow(double, double); 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 |
double hypot(double, double); 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 |
double sqrt(double); 该函数返回参数的平方根。 |
int abs(int); 该函数返回整数的绝对值。 |
double fabs(double); 该函数返回任意一个浮点数的绝对值。 |
double floor(double); 该函数返回一个小于或等于传入参数的最大整数。 |
随机数
有两个相关的函数:
- rand():返回一个伪随机数
- srand():生成随机数之前必须调用srand函数
举个例子:
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
int i,j;
//设置种子
srand( (unsigned)time(NULL) );
//生成10个随机数
for(i = 0; i < 10; i++)
{
//生成实际的随机数
j = rand();
cout<<"随机数:"<< j << endl;
}
return 0;
}
数组
C++允许从函数返回数组
字符串
函数 & 目的 |
---|
strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号,例如: string str1 = "runoob"; string str2 = "google"; string str = str1 + str2; |
strlen(s1); 返回字符串 s1 的长度。 |
strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。 |
strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
引用
引用和指针的区别:
- 不存在空引用,引用必须连接到一块合法的内存
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象
- 引用必须在创建时被初始化,但是指针可以在任何时候被初始化。
int i = 17;
//引用
int& r = i;
IO流
I/O库头文件
头文件 | 函数和描述 |
---|---|
该文件定义了cin、cout、cerr和clog对象,分别对应于标准输入流,标准输出流,非缓冲标准错误流和缓冲标准错误流 | |
该文件通过所谓的参数化的流操纵器(比如setw、setprecision),来声明对执行标准化I/O有用的服务 | |
该文件为用户控制的文件处理声明服务。 |
标准错误流(cerr)
预定义对象cerr是iostream类的一个实例。cerr对象是非缓冲流,且每个流插入到cerr会立即输出。(与流插入运算符<<结合使用)
//举个例子
#include "iostream"
using namespace std;
int main ()
{
char str[] = "Uable to read……";
cerr << "Eror message: "<< str << endl;
return 0;
}
标准日志流(clog)
是iostream类的一个实例。clog对象是缓冲的,每个流插入到clog都会现储存在缓冲区,知道缓冲区贴满或者缓冲区刷新才会输出。(与流插入运算符<<配合使用)
#include "iostream"
using namespace std;
int main( )
{
char str[] = "Unable to read....";
clog << "Error message : " << str << endl;
}
日志消息一般使用clog来输出,错误消息一般使用cerr来输出。
数据结构
定义结构
格式如下:
struct type_name{
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
}object_names;
//举个例子
struct Book{
char title[50];
char author[50];
int id;
}book1,book2;
typedef关键字
为创建的类型起一个别名,格式如下:
typedef struct Books{
char title[50];
char author[50];
int id;
}Book;
类和对象
类成员函数
成员函数可以定义在类定义内部,或者单独使用**范围解析运算符 :: **来定义。在类定义中定义的成员函数把函数声明为内联的,即使没有inline标识符。
举个用::的例子:
class Box{
public :
int length;
int breadth;
int height;
double getVolume();
};
//类外定义成员函数
double Box::getVolume(){
return length*breadth*height;
}
构造函数与析构函数
构造函数是在类的对象创建时进行执行,析构函数实在类的对象删除时进行执行。
析构函数比构造函数前面多了一个“ ~ ”
拷贝构造函数
是一种特殊的构造函数,在创建对象时,时使用同一类中之前创建的对象来初始化新创建的对象,通常用于:
- 通过使用另一个同类型的对象来初始化新创建的对象
- 复制对象把它作为参数传递给函数
- 复制对象,并从函数返回这个对象
如果在类中美哟定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数常见形式:
classname (const classname &obj){
//构造函数主体
}
友元
友元函数和友元类有权访问类的私有成员和保护成员,但是友元函数并不是成员函数,友元类也不是内部类。
举个例子:
class Box{
double width;
public:
double length;
friend void printWidth(Box box);
friend class ClassTwo;
}
void printWidth(Box box){
cout<<box.width<<endl;
}
class ClassTwo{
ClassTwo(){
cout<<Box::width << endl;
}
}
内联函数
内联函数通常和类一起使用。入如果一个函数是内联的,那么在编译时,编译器会把该函数代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数所有的客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
如果把一个函数定义为内联函数,需要在函数名钱放置关键字inline,在调用函数前需要对函数进行定义。
类中定义的函数都是内联函数,即使是没有inline说明符。
内联函数的规则
一个函数可以自已调用自已,称为递归调用,含有递归调用的函数不能设置为inline;
使用了复杂流程控制语句:循环语句和switch语句,无法设置为inline;
由于inline增加体积的特性,所以建议inline函数内的代码应很短小。最好不超过5行。
inline仅做为一种“请求”,特定的情况下,编译器将不理会inline关键字,而强制让函数成为普通函数。出现这种情况,编译器会给出警告消息。
在你调用一个内联函数之前,这个函数一定要在之前有声明或已定义为inline,如果在前面声明为普通函数,而在调用代码后面才定义为一个inline函数,程序可以通过编译,但该函数没有实现inline。比如下面代码片段:最终没有实现inline;
类中的静态成员
关键字:static
当类中的成员是静态时,无论创建多少个类的对象,静态成员都只有一个副本。
静态成员初始化可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
静态成员函数
静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符::就可以进行访问。
静态成员函数智能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,不能访问类的this指针。
继承
基类和派生类
格式如下:
class derived-class : access-specifier base-class{};
其中, access-specifier 指的是private/public/protect
访问控制和继承
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
继承类型
规则:
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多继承
各个基类用逗号分隔
重载运算符与重载函数
运算符重载
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。
举个例子:
//定义成普通的成员函数
Box operator+(const Box&){
};
//或者是定义成非成员函数
Box operator+(const Box&, const Box&){
};
不可重载的运算符列表:
- .:成员访问运算符
- .*, ->*:成员指针访问运算符
- :::域运算符
- sizeof:长度运算符
- ?::条件运算符
- #: 预处理符号
多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
重载是相同类相同函数名不同参数列表
多态是不同类相同函数名
关键字:virtual
虚函数
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
纯虚函数
您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
//举个例子
virtual int use() = 0;
=0告诉编译器,函数没有主体,该函数是一个纯虚函数。
文件和流
标准库
fstream
***头文件:***iostream 和 fstream
数据类型
数据类型 | 描述 |
---|---|
ofstream | 该数据类型表示输出文件流,用于创建文件并向文件写入信息。 |
ifstream | 该数据类型表示输入文件流,用于从文件读取信息。 |
fstream | 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。 |
打开文件
ofstream和fstream对象都可以用来打开文件进行写入操作,如果只是需要打开文件进行读操作,则只用ifstream。
open()成员函数的第一参数指定要打开文件的名称和位置,第二个参数dinginess文件被打开的模式:
模式标志 | 描述 |
---|---|
ios::app | 追加模式。所有写入都追加到文件末尾。 |
ios::ate | 文件打开后定位到文件末尾。 |
ios::in | 打开文件用于读取。 |
ios::out | 打开文件用于写入。 |
ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 |
如果需要两种或两种以上的模式结合使用,那么需要用到"|"来进行分隔
关闭文件
用close()函数。close()函数是fstream、ifstream、ofstream对象的一个成员。
文件位置和指针
重定义文件位置指针的成员函数:
istream:seekg() //seek get
ostream:seekp() //seek put
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。