2 变量和基本类型

c++ 定义了一套包括算术类型和空类型(void)在内的基本数据类型。

算术类型。
算术类型分为整型和浮点型。字符和布尔都算作整型。
C++规定了尺寸的最小值, char:1 short int:2 int:4 long int 4; float:4; double:8;long long int 8. bool类型最小尺寸未定义
带符号和无符号类型。
字符类型被分为了三种,char、signed char、unsigned char。 char的类型和编译器有关,有时候是无符号,有时候是有符号。建议使用char的时候指定有符号,无符号。不要用char。
使用规则:
1. 明确知道结果不可能为负数时,使用无符号数。
2. 使用int进行整数计算。
3. 算术表达式中不要使用char或bool,存放时才使用。
4. 执行浮点数运算选用double.float精度不够而且与double计算代价相差无几。

类型转换
1. 非布尔—>布尔,0则为false,其他为true
2. 布尔–>非布尔,false 为0,true为1
3. 浮点数->整数, 取小数点前面的那个
double a = 2177.20,a实际上可能为2177.1999,尤其是在qt里面使用double_Spinbox,需要进行精度补充。
5. 整数->浮点数,小数部分设置为0
6. 给无符号数一个超出他表示范围的值时,结果是对无符号类型表示的总数取mod。

   -1 --->255.取模和取余区别,正数相同,负数不同。1. 求商。 取下限  -1/256--->-1.   1/256 = 02. r = a - c * 商。 -1 - (-1)*256=255
同类型,有符号与无符号,直接内存拷贝。隐式转换拓展:
1. 算术运算需要转换1)类型不一样,先转换类型。表达式中最长类型为主,将其他类型位据均转换成该类型。若运算数中有double型或float型,则其他类型数据均转换成double类型进行运算。char<short≤int≤unsigned≤long≤unsigned long<float<double2)位数不一样,转换为高位的。值不变。3)类型和位数相同,有符号和无符号同时出现。转换为无符号的。  不要混用,会出现错误。
2. 赋值需要转换右边的转换为左边的类型。若右边的数据类型的长度大于左边,则要进行截断。右边长度小于左边,扩展。 有符号变为更长的有符号,值不变。
3. 输出转换printf会把char、short自动转换成int。  float自动转换成double强制类型转换(类型名)(表达式)。得到中间变量,原变量不变计算机中数据以补码形式存储。
1. 计算器被设计成只能计算加法,不能计算减法,符号位也参与计算
2. 用反码,有2个0
3. 用补码表示可以消除两个0的编码问题 1000 0000表示-128是人为规定的一个字节  -128-127
表示:
正整数,都一样
负整数,负数的反码是符号位不变其余原码逐位取反。补码,符号位参与,补码+1.
  1. 给有符号类型一个超出其范围的值,其结果未定义。
  2. 在程序中某处使用了算术类型的值,实际上需要其他类型。会转换成其他类型。 如 if (int a);

浮点数比较:fabs(x)<1e-6

字面值常量:
形如42的值叫做字面值常量。每个字面值常量都对于一种数据类型。
整型:十进制:20 八进制:024 16进制:0X14
浮点型:3.14 3.14E0 0. 0e0 .001
字符:‘a’ ‘0’----48 ‘\0’ ---- 0 ‘a’----97 ‘A’-----65
字符串:“hello world” 末尾加一个空字符’\0’
转义序列:不可打印(换行,回车等) 或特殊含义的字符
单引号('),双引号("),反斜线(\),问号(?)
\123 八进制,超过3个其他不算 \x12 16进制

添加前后缀可指定字面值的类型。u8"hi"  utf8的字面值    42ULL  42无符号长长整型。

布尔字面值:true false
指针字面值:nullptr
c中是这样定义NULL: #define NULL ((void *)0)

字符常量,用单引号括起来的单个普通字符或转义字符
字节数:4 int val = ‘p’

初始化和赋值:

列表初始化:
用{}来初始化变量。
默认初始化:
如果定义时没有初始化,则是默认初始化,默认值由变量类型以及定义变量的位置决定。 定义在函数体内的内置类型变量不被初始化,外的被初始化为0. 没有被初始化,错误不可知。

变量声明和定义。
程序分为多个文件,为了在文件间共享代码,需要支持分离式编译。声明使得名字为程序所知,一个文件如果想要使用别处定义的名字则必须包含对那个名字的声明。而定义负责创建与名字关联的实体。
声明:多个cpp文件,用同一个变量。
extern int i; 声明 规定类型和名字,
extern int i = 0; 定义。 分配空间和初值。

类声明:class 类名;

  1. 可以仅仅声明类而暂时不定义它,这种声明被称为前向声明。在它声明之后定义之前该类是个不完全类型。
    2.不完全类型只能在非常有限的情况下使用:可以定义指向这种类型的指针或引用,也可以作为一个已经声明(但没有定义)的函数的参数或返回类型。
    3.对于一个类来说,在创建它的对象前必须首先完成类的定义,而不能仅仅被声明。否则编译器就无法了解这样的对象需要多少存储空间。类似的,类也必须首先被定义,然后才能用引用或者指针访问其成员。
    4.对于类的静态成员,直到类被定义之后数据成员才能被声明成这种类类型。我们必须首先完成类的定义,然后编译器才能知道存储该数据成员需要多少空间。因为只有当类全部完成后类才算被定义,所以一个类的成员类型不能是该类自己。然而,一旦一个类的名字出现后,它就被认为是声明过了(但尚未定义),因此类允许包含指向它自身类型的引用或指针。

函数声明:
与定义的区别是没有函数体,用分号替代。
可以不包含形参名字,但为了可读性,最好还是写上。

如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。

复合类型:

  1. 引用
    一般指左值引用,为对象起的别名。
    右值引用:c++ 11新特性,用&&来获得,只能绑定即将销毁的对象。常数、表达式、函数返回值。主要是为了提高效率。相当于延长了生命周期。
    疑惑:return 做了什么? https://blog.csdn.net/jeff_/article/details/48915759
    左值:变量等,等号左边,长久保存。
    右值:临时值,等号右边,短暂存在。
    右值引用可以减少拷贝的次数,提高效率。
    std::move 将右值引用绑定到左值上。我们可以销毁一个移后源对象,也可以赋予新值,但不能使用一个移后源对象的值。
    对右值进行浅拷贝,右值对象中的指针直接赋值给新的对象,然后将右值对象的指针赋NULL.

  2. 指针:一个特殊的变量,存储地址
    遇到指针,都应该问三个问题:指针的类型,指针所指向的类型,指针的值
    nullptr指针作用:1、解决函数调用歧义 2、构造函数重载
    2个指针比较 == 指针的值比较。

复合类型的声明:
从右往左读,离变量名最近的符号对变量类型有最直接的影响。
指针数组 : *p[n]
数组指针 :(*p)[n] 可以指向一维数组,也可以指向二维数组,此时+1 表示到了下一行
二级指针:指向指针数组
sizeof 数组: 数组的大小 指针:4或8

const 限定符
const变量和普通的变量一样,特殊的是初始化后值不能改变。
当以编译时初始化的方式定义一个const变量时,如const int bufSize = 512; 编译的时候,会把所有用到该变量的地方替换对应的值。默认仅在当前文件里有效,当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
要多文件共享,需要定义和声明的时候加extern。
const引用:通常情况下,引用只能绑定到对象上,而不能与字面值或某个表达式的结果绑定在一起。const 引用特殊,当绑定到另一个类型时,会构造一个临时变量,而绑定到临时变量上。
const 指针:int * const p; 从右往左读,就近原则。
指向常量的指针: const int *p; 指针不能修改里面的值。

顶层const
顶层const表示指针本身是个常量,底层const表示指针所指向的对象是个常量。
顶层const可以表示任意对象是常量,底层const与指针和引用等有关。
拷贝操作时,底层const的限制不能忽视。拷入和拷出的对象必须有相同的底层const资格,或者2个数据类型必须能够转换。非常量可以转换为常量,反过来不行。
顶层const不影响拷贝操作。

类型别名:
typedef;
定义类型别名。 与const结合会有意想不到的效果。 新的const 对象。
typedef char * pstring. const pstring cstr; cstr是一个指向char的常量指针。
##可以这样来理解:typedef int integer;将typedef去掉,那就是个变量的定义,这儿即定义了一个int型的变量integer,考虑这个integer是什么类型的,那么这个typedef语句就是将integer定义为这个类型的。将typedef int (*func_pointer)(const&, int);中的typedef去掉,就成了一个函数指针定义,即func_pointer被定义为函数指针类型变量,那么原来的typedef即将func_pointer定义为函数指针类型.

auto:
让编译器通过初始值来推断变量的类型(发生在编译期)。 特殊:当引用作为初始值时,真正参与的是引用对象的值。
1. 声明为auto(不是auto&)的变量,忽视掉表达式顶层的const。即对有const的普通类型(int,double)忽视const,对常量指针(顶层const)变为普通指针,对指向常量(底层const)的常量指针(顶层const)变为指向常量的指针(底层const)。
2. 声明为auto&的变量,保持表达式的顶层const或volatile属性
3. 若希望推导的是顶层const,加上const,即const auto。
容器:
特定类型对象的集合。

自定义数据结构:
数据结构就是把一列相关的元素组合起来,然后使用它们的策略和方法。
定义类的时候,类体}后面要加上分号。定义一个类,也就是定义了一个类型。定义类型时一般不进行存储分配。定义对象时将为其分配存储空间。命名空间不需要加。
C++ 11新标准,可以为类内的数据成员提供一个类内初始值。花括号或=,不可圆括号。
头文件:预处理变量无视作用域。 ifndef,一般以类名大写表示头文件 _H.
#define 和 const区别:
1. define是在编译的预处理阶段起作用,而const是在 编译
2. const有类型检查,可以调试。

结构体
1. 对齐字节:
32位:4 64位:8 #progma pack(n) 指定对齐字节值
起始地址必须能整除成员变量的大小
结构体的大小 必须是最大元素的整数倍
2. 共用体(联合体) union 取最大值,所有成员相对于基址地址的偏移量为0。可以用来判断大小端。大端:高字节放在低地址。小端,低字节放在低地址。
3. 枚举 缺省值为0,1,2…;若赋值,自动加1
赋值时,需要赋枚举里面的值,不能直接1,2

3 字符串、向量和数组

using声明。
使用using声明后,每次调用时就不需要再加前缀了。
using std::cin;
头文件不应该using声明,可能会产生难以预料的名字冲突。

标准库类型 string
string 表示可变长的字符序列。
1. 直接初始化和拷贝初始化。使用等号的是拷贝初始化,否则是直接初始化。
2. 读写string 对象。 cin >> str;
cin 时,string会自动忽略开头的空白(空格,换行,制表符),遇到下一个空白停止。string对象返回运算符左侧的运算对象作为结果,因此可以连续输入。
cin >> str1 >> str2;
while ( cin >> str) 遇到文件结束符或非法输入结束。
3. getline,遇到换行符结束,换行符会从缓冲区刷掉,不会写入string里。
4. string::size_type, size的返回值,无符号类型。 不要和有符号比较。
5. string对象比较。 相同,短的< 长的。
6. string对象相加,与字面值混用时,+ 两侧必须有一个string。 字符串字面值和string是不同的类型。
7. 使用C++版本的C标准文件 #include cname 而不是name.h
8. 下标访问,[]返回的是该位置上字符的引用
9. size, length返回string真实长度,即便里面有\0,使用strlen可以截断,去掉\0

标准库类型vector
vector存放的是某种给定类型对象的可变长序列。
1. vector是类模板,需要传入额外信息。 vector
2. 列表初始化 {} 如果不能列表初始化,则是值初始化。
vector v(10, “hi”);
3. 值初始化 () vector vec(10, -1); 生成10个-1的元素,如果没写-1,则为0. string则默认初始化。
二维数组:vector<vector> vec(m, vector(n,0)); m*n的二维数组,所有元素都为0
4. []只能访问,不能去添加元素。
5. 插入时写emplace_back,而不是push_back;
push_back()右值时就会调用构造函数和转移构造函数。需要先构造临时对象。
emplace_back在插入的时候直接构造,就只需要构造一次即可。不需要构造临时对象,右值引用参数。
若push_back/emplace_back传递的参数为已被构造的对象,则没有差别。
6.vec.size() 当前容器所存储的元素个数
7.vec.capacity() 容器在分配新的存储空间之前能存储的元素总数
6. 内存扩充策略:满了的时候,成倍扩充,然后拷贝原有数据到新内存,释放原内存。
7. 内存泄漏:clear()和erase(),resize()只改变size,不改变capacity。防止:ivec.swap(vector(ivec)); 定义一个临时变量,交换内容
8. vec.insert(vec.begin()+i,a);在第i个元素后面插入a;
9. 当n大于当前元素个数,resize和reserve都会capacity。根据分配策略,可能会有更大的一块。resize未指定参数,按类型默认初始化,添加元素。而reserve不会添加元素。
n < 当前元素个数,resize删除多余的,capacity不改变。而reserve什么也不做。
10. find(vec.begin(), vec.end(), i) != vec.end();
11. vec.erease(vec.begin(), vec.begin()+1); 删除第一个元素 左闭右开
12. pop_back() 删除最后一个元素,尽量不要从中间删除

测试网站:https://cpp.sh/

迭代器
1. 所有标准库容器都可以使用迭代器,只有少数几种支持下标运算符。
2. v.end();表示尾元素的下一位置,当容器为空时,begin==end
3. *iterator 返回所指元素的引用
4. iterator->mem 等价于 (*iterator).mem
5. 迭代器类型 iterator(读写) const_iterator(只读) cbegin返回const_iterator.
6. erase删除容器后,返回下一个迭代器。

数组
1. 定义的时候,数组维度必须可知。为常量表达式。
2. 数组未初始化的时候,为默认初始化
3. 部分初始化的时候,类似vector值初始化,其他值采用默认值
4. 数组不允许拷贝和赋值
5. 复杂的数组声明,从数组的名字按照从内向外的顺序读,先右后左。
6. c风格字符串。strlen返回p的长度,空字符不计算在内,必须有空字符,不然会有错误。
使用string比c风格更安全和高效。
7. 使用c_str(),返回指向一个空字符结尾的字符数组的常量指针。string内部的,以size计算。尽量使用标准库类型而非数组。
8. 多维数组,指的是数组的数组,按照名字从内到外的顺序阅读。

4. 表达式

表达式由一个或多个运算对象组成,对表达式求值将得到一个结果。字面值和变量是最简单的表达式,结果是他们的值。

左值和右值
右值的时候,用的是对象的内容;左值的时候,用的是对象的身份。

算术运算符满足左结合律,如果优先级相同,按照从左往右的顺序。

括号无视优先级与结合律。

运算对象的求值顺序和优先级和结合律无关。优先级只是规定了运算对象的组合方式。
int i = f1() * f2(); 未说明f1还是f2先计算
cout << i << ++ i << endl; 未定义

算术运算符—取模:
早期允许m%n的符号匹配n的符号,并且商向负无穷一侧取整。C++新标准已经禁用,除了-m导致溢出的特殊情况,其他时候:(-m)/n ,m/(-n) == -(m/n); m%(-n)=m%n;(-m)%n=-(m%n). 商一律向0取整。

逻辑和关系运算符
与和或都是先算左边再算右边,如果左边可以确定整个的值,则不会计算右边的(短路求值)。
关系运算符的求值结果是布尔值,if (i < j < k)

赋值运算符
赋值运算满足右结合律,返回的是左侧运算对象。
ival = ijal = 0; 都为0
优先级低于关系运算符,if里面需要加括号

递增和递减运算符
前置版本,返回改变后的对象。
后置版本,返回改变前的对象。 (会生成一个副本,减低效率 j++,一般用前置版本)
使用*p++; 简洁,递增高于解引用。
*beg = toupper(*beg++); 未定义错误,不知道先算左边的还是右边的。

成员访问运算符
p->mem 等价于(*p).mem 加括号是因为解引用优先级低于.

条件运算符
cond ? exp1 : exp2
首先求cond的值,如果为真对exp1求值并返回其值,否则对exp2求值并返回其值。
在输出表达式中运用条件运算符时,需要加括号,优先级较低。

逗号运算符
常用于for循环中。

强制类型转换
static_cast:把 expression 转换为 type-id 类型,但没有运行时类型检查来保证转换的安全性。
  主要用法如下:
    (1)用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
        进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
        进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
    (2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
    (3)把空指针转换成目标类型的空指针。
    (4)把任何类型的表达式转换成void类型。
const_cast: static_cast 不能将 const int* 转成 int*,const_cast 就可以. static_cast不支持底层const。
C语言的强制类型转换有时候会有问题,推荐使用C++类型的。

5.语句

空语句
;加了空语句需要加注释

条件语句
多层嵌套,可以提逻辑,减少嵌套层数
else与离他最近的未匹配的if匹配

switch语句
1. switch对表达式求值,然后值转变为整型
2. case标签必须是整型常量表达式
3. 如果表达式和某个case匹配,直到switch结尾或遇到break结束。接着执行switch之后的语句。
4. 一般加default,表示我们考虑到了这个情况。
5. switch内部定义的变量,如果没有初始化,其他分支可以用。初始化了其他分支不可以用。

迭代语句
1. while语句,定义在条件部分和循环体内的变量,每次迭代都经历创建和销毁的过程。
2. 传统for语句。 for (init;condition;expression)
3. 范围for语句。
for(declaration: expression).
expression必须是一个序列。
declaration定义一个变量,每次迭代都重新定义变量,并将其初始化序列中的下一个值。
对范围for语句,不能增加vector对象的元素。因为for(auto r : v) 等价于 for(auto beg = v.begin(), end = v.end(); beg != end; ++beg)。

跳转语句:
1. break语句,终止离最近的while for do while switch语句,并执行其之后的第一条语句。
2. continue,终止里最的的循环中的当前迭代并立即开始下一次迭代。对于传统for,继续执行for语句头的expression;对于范围for,用序列中的下一个元素初始化循环控制变量。
只有switch嵌套在迭代语句里,才能用continue。

6.函数

函数是一个命名了的代码块,通过调用函数执行对应的代码。函数可以重载,同一个名字可以对应几个不同的函数。

参数传递:
每次调用函数时会创建它的形参,并用传入的实参对形参进行初始化,形参的初始化和变量的初始化一样。
使用引用可以避免拷贝,提高效率,最好用常量引用。
尽量使用常量引用:1. 不使用常量引用会误导读者:函数可以改变引用的值 2. 非常量引用会缩小范围,引发错误 3. 函数内调用另一个函数,无法直接使用。

数组形参:
int * int[] int[10] 都等价的,10只是表示期望,实际不影响
二维: int(*p)[10]; int p[][10] 第二个不可省略,一定要相等。

main命令行参数:
prog -d -o -ofile daa0 argc = 5, argv[0]表示程序的名字,argv[1]表示实参。

返回值
void函数,使用return; 末尾会自动执行return;
有返回值的函数,要写return 1;不然会有未定义的错误.主函数main例外,如果结束的时候没有,编译器会隐式的插入一条返回0的return语句。
返回一个值的方式和初始化一个变量或形参的方式完全一样,返回的值用于初始化调用点的一个临时变量。
#### 不要返回局部对象的引用或指针,存储空间被释放,指向为无效的内存区域。
调用运算符的优先级和点和箭头的相同,符合左结合律。
auto sz = shortString(s1, s2).size();
数组和函数无法拷贝。
声明一个返回数组指针的函数:Type (*fun(para))[length].
使用decltype: int odd[] = {1,2,1,3};
decltype(odd) *func(int i);

函数重载:
函数名相同,形参列表不同。返回值不同不构成重载。
编译器优化,传递一个非常量对象的指针时,编译器优先选用非常量函数。。
如果在内层作用域声明名字,将隐藏外层作用域中声明的同名实体,在不同作用域内无法重载函数名。变量名也可隐藏函数名。

默认实参:合理设计顺序,默认值的形参出现在后面。一般出现在声明中,可以添加默认实参(需要保证该右侧的形参都有默认值),但不能修改一个已经存在的值。
string func(int, int, char = ‘c’);
string func(int, int, char = ‘a’); //错误,重复声明
string func(int = 80, int = 80, char = ‘a’); // 正确

内联函数:可避免函数调用的开销。
是对编译器发出的一个请求,编译器可以忽略请求。
适用于规模较小,流程直接,频繁调用的函数。一个75行的不太可能内联展开。

constexpr函数:
在编译的时候就能得到其返回值的函数。编译器constexpr函数直接转换成其返回值,因此,constexpr函数都是被隐式地定义为内联函数。使用constexpr关键字来修饰constexpr函数。
如constexpr int func(int i);
函数的返回类型和形参的类型必须是字面型类型,函数体内有且只有一条return语句。
函数体内可以包含其他语句,但需要不执行任何操作,如空语句,类型别名等。
允许函数的返回值并非一个常量,当实参非常量时。

调试帮助:
assert预处理宏,当表达式结果为假时,输出信息并终止程序的执行。为真时,什么也不做。
NDEBUG预处理变量,assert行为依赖了这个变量,如果定义了这个,则assert什么也不做。可以使用这个变量编写自己的条件调试代码。
C++编译器定义:
_ _ func _ 当前函数的名字,const char 数组
C++预处理器定义:
_ _ FILE
_ 存放文件名
_ _ LINE_ _ 存放当前行号
_ _ TIME_ _ 存放文件编译时间
_ _ DATE_ _ 存放文件编译日期

函数匹配:
1. 选定本次调用的重载函数集,集中的函数称为候选函数。需要与调用函数同名,声明在调用点可见。
2. 根据实参,选出可行函数。需要形参数量与实参数量相同,实参与形参类型相同,或者能转换为形参类型。
3. 从可行函数中寻找最佳匹配。
基本思想:实参类型与形参类型越接近,匹配越好。
含有多个形参的函数匹配。
1. 该函数每个实参的匹配性都不劣于其他可行函数需要的匹配
2. 至少有一个实参的匹配优于其他可行函数的匹配。
如果检查了所有实参之后没有一个函数可以脱颖而出,则调用错误,编译器会报二义性错误。

实参类型转换等级

  1. 精确匹配:1)实参和形参类型相同。2)实参从数组类型或函数类型转换为对于的指针类型。3)向实参添加/删除顶层const.

  2. 通过const转换实现的匹配 将指向非常量类型的指针或引用转换成对应的常量类型的指针或引用。
    非常量对象实参优先使用非常量形参函数。

  3. 通过类型提升实现的匹配。 整型提升

  4. 通过算术类型转换或指针转换实现的匹配。
    算术转换是把一种算术类型转换成另一种算术类型。
    指针转换:0或nullptr能转换成任意指针类型。指向任意非常量的指针能转换成void*.指向任意对象的指针能转换成const void *;继承关系间指针的转换。

  5. 通过类类型转换实现的匹配。

类型提升
ff(int); ff(short);
ff(‘c’) 直接调用ff(int);
算术类型:所有算术类型转换的级别都一样。
ff(long); ff(float);
ff(3.14); 错误,二义性调用。

函数指针
指向函数,函数的类型与返回类型和形参类型共同决定,与函数名无关。
返回指向函数的指针
using f = int(int*,int); f是函数类型,不是指针
using pf = int (*p)(int *, int) pf是指针类型。
返回类型不会自动地转换为指针,需要我们显式的转换。
pf f1(int);正确
f f1(int); 错误,f是函数类型
f * f1(int); 正确,函数指针。

7.类

类的基本思想是数据抽象和封装。
1)数据抽象是一种依赖于接口和实现分离的编程技术。类的接口包括用户所能执行的操作;类的实现包括类的数据成员、负责接口实现的函数体以及各种私有函数。

2)封装实现了类的接口和实现的分离。封装后的类隐藏了它的实现细节,即用户只能使用接口而无法访问实现部分。

类想要实现数据抽象和封装,首先需要定义一个抽象数据类型。在抽象数据类型中,由类的设计者负责类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无需了解类型的工作细节。-----数据抽象。

定义在类内部的函数是隐式的内联函数。

成员函数含有this指针,this是一个常量指针,指向这个对象,无法修改。顶层const ,const靠近指针。
1)const成员函数,const关键字隐式修改this指针的类型。
由data * const p 变为 const data * const p;
2)const成员函数无法改变对象的内容
3)常量对象,以及常量对象的引用和指针都只能调用常量成员函数。
4)const成员函数的定义也要在后面加const

构造函数:
1)构造函数负责类的初始化
2)构造函数没有返回值
3)构造函数不能声明为const。 当创建一个const对象时,直到构造函数完成初始化后,对象才获得const属性。

默认构造函数
1)默认构造函数就是在调用时不需要显示地传入实参的构造函数。无参数的”和“带缺省参数的都是,不要同时出现。一般情况下,默认构造函数是一个空的函数体。
2)如果类没有显示的定义构造函数,则编译器会隐式的定义一个默认构造函数,称为“合成的默认构造函数”。
a.如果存在类内的初始值,则用它来初始化
b.否则,默认初始化该成员。内置类型在函数体内未知值。如果类的对象没有显式地初始化,则其值由类确定。

不能依赖于合成的默认构造函数

  1. 编译器只有在发现类不包含任何构造函数的情况下才会替我们生成一个默认的构造函数。
  2. 含有内置类型或复合类型成员的类如果没有默认值,采用的默认初始化,值未定义。
  3. 编译器无法为某些类合成默认的构造函数。如果类中包含一个类类型没有默认的构造函数,则无法初始化该成员。

想要合成的默认构造函数。
sales_data() = defaultl;

如果没有在构造函数的初始化列表中显示地初始化成员,则该成员将在构造函数体之前执行默认初始化。
如果成员是const、引用或者属于某种未提供默认构造函数的类类型,则必须通过构造函数初始化列表为这些成员提供初值。
建议使用构造函数初始值:1.底层效率问题 2.一些数据成员必须初始化,可以避免某些意想不到的编译错误。

成员初始化顺序与在类内定义的出现顺序一致,与初始化列表的顺序无关。

sales_data obj(); //声明了一个函数而非对象

只接受一个实参的隐式转换机制:

  1. 只允许一步类类型转换。如item.combine(“sadfa”);无法将char *转换为string,然后再转为sales_data.
  2. 可以用关键字explicit,只对一个实参的构造函数有效,不支持隐式构造。只能在类内声明的时候使用,类外定义的时候不能重复。

class默认访问权限是私有,struct默认访问权限是公有。

友元:
类可以允许其他类或者函数访问它的非公有成员。友元,增加一条以friend开头的函数声明。
一般最好在类定义开始结束前的位置集中声明友元。
友元的声明仅仅指定了访问权限,而非一个通常意义上的函数声明。如果希望类能够调用函数,则要再加个函数声明。
友元类:友元类的成员函数可以访问包括非公有成员在内的所有成员。
友元关系不具备传递性。
每个类负责控制自己的友元类或友元函数。

类的其他特性:
可变数据成员:变量声明成mutable,任何时刻都可以更改它,即便是在const成员函数里。

类的声明:
可以只声明类而暂时不定义它,称为前向声明。称为不完全类型,可以用于:定义指向这种类型的指针或引用,也可以声明(不可以定义)以不完全类型作为参数或者返回类型的函数。

类的作用域:
一个类就是一个作用域,在类外定义成员函数时必须同时提供类名和函数名。在类的外部,成员的名字被隐藏起来了。
一旦遇到类名,定义的剩余部分就在类的作用域之内的。这里指参数列表和函数体。
函数的返回类型在函数名之前,所以要加作用域。

名字查找和类的作用域:
类的定义分为2部分:1.编译成员的声明  2.直到类全部可见后才编译函数体。这样可以简化类代码的组织方式,定义函数的时候可以处理所有成员变量。内层作用域可以重新定义外层作用域的名字。但是如果在类中,成员使用了外层作用域的某个名字,而该名字代表了一种类型,则类不能再重新定义该名字。
成员函数使用的名字查找顺序:
1. 在成员函数内部查找声明
2. 在类内查找
3. 在类外查找。
可以隐藏同名变量。
想要使用外部的,如::height;或类内的:this->height;

类的静态成员:

  1. 与类关联而不是与类的对象关联。
  2. 静态成员函数不能被声明为const,不包含this指针。
  3. static关键字只出现在类内部的声明语句中,无法出现在类外部。
  4. double Account::interestRate = initRate(); 虽然initRate是私有的,也能这样用。从类名开始,这条语句剩余的部分就都位于类的作用域之内了。
  5. 静态成员可以是不完全类型。如在class Bar里面定义static Bar mem1;

8. IO库

9. 顺序容器

顺序容器:
通常,使用vector是最好的选择,除非有更好的理由选择其他容器。

迭代器:左闭右开区间。

类型别名:使得使用和机器无关
size操作返回是string::size_type类型的值。
difference_type使用来表示两个迭代器之间的距离的。
对于+1,-1问题,如果2边是闭区间,则是right-left+1;如果有一端是开的,则right-left。
value_type:元素类型。
每个STL容器类,都有一句相同的代码:typedef T value_type;
typedef关键字就是给这些数据类型起一个别的名字,然后用这个自己起的名字来定义变量。
用处:1. 应该使用指示了大小和符号typedef来代替基本类型,见名知意。
reference:元素左值引用。

begin和end
begin返回第一个元素的迭代器,end返回尾元素之后位置的迭代器。
r返回反向迭代器,c返回const迭代器。
当不需要写访问时,应使用cbegin和cend.

vector a (10); 10个元素值为0

array
array<int, 10>,使用时必须指定元素类型和大小。
内置数组无法进行拷贝和对象赋值操作,但是array可以。需要保证元素类型和大小一致。

赋值和swap:
assign仅适用于顺序容器,会替换原来的值,不能传递给调用assign容器的迭代器。
swap交换2个相同类型容器的内容。不会进行元素拷贝,除array外,不会导致指向容器的迭代器、引用和指针失效。

容器元素时拷贝,当我们用一个对象初始化容器时,或讲一个对象插入到容器时,我们实际上放入的是对象值的拷贝,而不是对象。

insert将元素插入到所指定的位置之前。
同一次插入多个值时,不会改变顺序。
iter = lst.insert(iter, word); 多次push_front;
返回的是新插入元素的迭代器

front,back,下标和at返回的是引用。
c.front() = 42;
auto &v = c.back(); 可以改变
auto v2 = c.back(); 无法改变

at可以对越界进行检查,越界会抛出out_of_range异常。下标访问不会。

c.erease(p);删除p所指定的元素,返回一个指向被删除元素之后元素的迭代器。
如果迭代器失效,为安全考虑,不要再使用它。

管理容量的成员函数:
c.shrink_to_fit(); 只适用于vector。string和deque。将capacity减少为size大小。只是一个请求,标准库并不保存归还内存。
capacity和reserve只适用于vector和string。
c.capacity();不重新分配内存空间,c可以保存元素个数。
c.reserve(n);分配至少能容纳n个元素的内存空间。
resize只改变size,不改变capacity。

额外的string操作:
substr, 返回一个string,是原始string的一部分或全部的拷贝。
传递一个开始位置和计数值。
string s1 = s.substr(0, 5);如果开始位置大于s大小,会抛出out_of_range异常。如果开始位置+计数值大于s大小,则只会拷贝到结尾。

find返回一个npos,定义为-1;
auto f = s.find(“name”);
数值转换:

  1. 转换为string. to_string(i);
  2. string转换为其他: stod(dValue); stoi,stol,stoul等。
    如果string不能转换为一个数值,会抛出invalid_argument异常。如果转换得到的数值无法用任何类型来表示,会抛出out_of_range异常。

11.关联容器

关联容器的元素是按照关键字来保存和访问的。

2个主要的关联容器:map和set.
map中的元素是一些关键字-值(key-value)对,关键字起到索引的作用,值则表示与索引相关联的数据。
set中每个元素只包含一个关键字,支持高效的关键字查询操作:检查一个给定关键字是否在set中。

标准库提供了8个关联容器,1. 关键字是否可以重复 2. 是否有序。
multimap表示可以重复,unordered_map表示无序。

map是关键字-值对的集合,set是关键字的集合。

pair类型:
一个pair保存2个数据成员。
pair<T1, T2> p;进行值初始化
pair<T1, T2> p(v1, v2); first是v1, second是v2
make_pair(v1, v2); 返回一个用v1和v2初始化的pair,类型通过v1和v2的类型推断得到。
p1 relop p2; relop表示关系运算符,只有first和second同时满足时,才满足。

关联容器操作:
key_type: 关键字类型
mapped_type:关键字关联的类型,只适用于map
value_type: set是key_value;
map是pair<const key_value, mapped_type>因为关键字不能改,所以是const.
当解引用一个迭代器时,返回的是value_type类型的引用。
map的first不可更改。
set的迭代器都是const,不可更改值。
通常不对关联容器使用泛型算法。

添加元素:

  1. 向map和set中插入一个已经存在的元素,对容器没有任何影响。
  2. 向map添加一个元素:insert的返回值:对于一个不包含重复关键字的容器,返回一个pair。第一个元素表示给定关键字元素的迭代器,第二个元素表示是否插入成功的bool值。如果已经在容器中,bool是false。如果是重复的容器,则返回给定关键字元素的迭代器。

删除元素:
1 . erase函数,返回删除元素的个数。

map下标操作:
map和unordered_map容器提供了下标运算符和一个对应的at函数。set不支持下标,因为没有与关键字对应的值。multimap也不支持小标操作,因为有多个值与关键字对应。
map进行下标操作时,如果关键字不再map里,则会添加到map中,并进行值初始化。所以只能对非const的map使用下标操作。
c[k]; 返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,并进行值初始化。
c.at(k);访问关键字为k的元素,带参数检查;若k不在c中,抛出一个out_of_range异常。

访问元素:
find,count,count会返回次数,如果不需要计数,最好用find.
对map使用find代替下标操作。

无序容器:
不是使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的==运算符。

无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。无序函数的性能依赖于哈希函数的质量和桶的数量和大小。

12. 动态内存

在C++中,动态内存管理是通过一对运算符完成的。new,在动态内存中为对象分配一个空间并返回一个指向该对象的指针。delete,接受一个动态对象的指针,销毁该对象,并且释放与之相关的内存。
shared_ptr允许多个指针指向同一个对象。unique_ptr则独占所指向的对象。weak_ptr是伴随类,是一种弱引用,指向shared_ptr所管理的对象。

shared_ptr类
与vector一样,使用的时候需要传入指针可以指向的类型。


每个shared_ptr都有一个引用计数,当我们用一个shared_ptr初始化另一个shared_ptr时,或将它作为一个参数传递给一个函数,以及作为函数的返回值时,它所关联的引用计数就会递增。当我们给一个shared_ptr赋予新值或者shared_ptr被销毁时,引用计数会递减。

每次shared_ptr销毁时,会调用shared_ptr的析构函数,将引用计数-1,如果此时变为0,则会销毁对象并释放所占用的内存。

使用动态内存的一个常用原因是允许多个对象共享相同的状态。

直接管理内存
使用new动态分配内存和初始化对象。
int *p1 = new int; 默认初始化,值未定义
int *p2 = new int(); 值初始化为0;
int *p3 = new (nothrow) int; 如果分配失败,会返回一个空指针,不会抛出bad_alloc异常。

释放动态内存
delete会销毁给定指针指向的对象;释放对应的内存。
传递的指针必须指向动态分配的内存,或者是一个空指针。释放一块并非new分配的内存或者将相同的指针值释放多次,行为是未定义的。
delete之后,指针变为悬空指针,指向一块曾经保存数据对象但现在已经变得无效的内存的指针,应该delete后将其赋nullptr。

接收指针参数的智能指针构造函数是explicit的,必须使用直接初始化的方式。
shared_ptr p1= new int(1024); 错误;
shared_ptr<int p2(new int (1024)); 正确。


不要混合使用普通指针和智能指针
如果混合使用的话,智能指针自动释放之后,普通指针有时就会变成悬空指针,当将一个shared_ptr绑定到一个普通指针时,我们就将内存的管理责任交给了这个shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了。
也不要使用get初始化另一个智能指针或为智能指针赋值

unique_ptr
某个时刻只能有一个unique_ptr指向一个给定对象,由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。

不能拷贝unique_ptr有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr.最常见的例子是从函数返回一个unique_ptr.

weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。
weak_ptr的操作

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock,此函数检查weak_ptr指向的对象是否存在。如果存在,lock返回一个指向共享对象的shared_ptr,如果不存在,lock将返回一个空指针。

动态数组:

进度:看到423页。

需要融合:
4. 在可以使用const的时候,都要添加const
5. const函数定义的时候,也要加const

C ++ primer相关推荐

  1. 《C++primer》第一章--开始

      之前开始读<C++primer>,想着读书不动笔不如不读书,于是就想做一个读书笔记的内容,于是就想起了写一个<C++primer读思录>的一个专栏.一是为了给自己平时读书做 ...

  2. C++ Primer 读书笔记 (1)

    我的<C++ Primer>扉页记着购书的日期和地点:C++ Primer 2009.11.28购于西区求索.那时对于这本厚书一直心怀敬畏,仿佛是眼前的一座大山.那时,虽然已经大四,但是对 ...

  3. C++ Primer(第4版)(评注版)

    <C++ Primer(第4版)(评注版)> 基本信息 原书名: C++ Primer (4th Edition) 原出版社: Addison-Wesley Professional; 4 ...

  4. C++ Primer英文版(第5版)

    <C++ Primer英文版(第5版)> 基本信息 作者: (美)李普曼(Lippman,S.B.) (美)拉乔伊(Lajoie,J.) (美)默Moo,B.E.) 出版社:电子工业出版社 ...

  5. C++ Primer 学习笔记(第四章:表达式)

    2019独角兽企业重金招聘Python工程师标准>>> ##C++ Primer 学习笔记(第四章:表达式) [TOC] ###4.1 基础 左值和右值: 当一个对象被用作右值的时候 ...

  6. C++ Primer 第三版 读书笔记

    1.如果一个变量是在全局定义的,系统会保证给它提供初始化值0.如果变量是局部定义的,或是通过new表达式动态分配的,则系统不会向它提供初始值0 2.一般定义指针最好写成:" string * ...

  7. c++ primer,友元函数上的一个例子(By Sybase)

    本文试图解释c++ primer Screen 和 Window_Mgr的例子,为什么将两个类放在两个文件中无法编译? 将两个类写在同一个文件中,通过三个例子解释问题: 第一种写法问题: 编译到Scr ...

  8. 《C Primer Plus(第6版)中文版》一第1章 初识C语言1.1 C语言的起源

    本节书摘来自异步社区<C Primer Plus(第6版)中文版>一书中的第1章,第1.1节,作者 傅道坤,更多章节内容可以访问云栖社区"异步社区"公众号查看. 第1章 ...

  9. C++ primer学习方法

    C++ primer学习: 第一次可以跳着看.关键是要尽快用起来,在使用中熟练,而不是在细节中迷失. 以C++ Primer第五版为例,第一遍读的时候: Part1也就是前七章,除了6.6,6.7节, ...

  10. 程序清单3.3_bases.c程序_《C Primer Plus》P37

    /* bases.c -- 以十进制.八进制.十六进制形式输出 100 */// bases.cpp : 定义控制台应用程序的入口点. // /*时间:2018年06月03日 23:23:06代码:程 ...

最新文章

  1. html5语音云,搜狗语音云开放平台
  2. boost::exception_detail::refcount_ptr的测试程序
  3. pytorch微调bert_北大、人大联合开源工具箱UER,3 行代码完美复现BERT、GPT
  4. NoteRenderer.render logic
  5. GPS基站架设完整操作流程
  6. python导入函数模块 为什么会打印两次_5.1.2Python从模块导入函数
  7. element走马灯自动_详细element-ui的走马灯carousel轻松实现自适应全屏banner详细过程...
  8. 期许伟大-基于CMMI的过程改进之道探索
  9. db9针232接口波特率标准_RS-232串口使用的DB9螺钉,螺纹规格是什么?
  10. word树状分支图_word绘制树形图
  11. Java DAO、Service、web理解之DAO层
  12. 十年寻伯乐,只为指路人
  13. FPGA——1位全加器和4位全加器的实现
  14. Vue 前端导出Excel表格,多级表头合并
  15. Beta测试应用程序完整指南
  16. Unity DOTS学习导航
  17. 错误:The Operation Couldn't be Completed
  18. 如何获取特定用户组内的无效账户?
  19. nodejs基于Vue旅游网站的设计与开发express前端源码
  20. X64dbg 2021最新版 中文乱码解决

热门文章

  1. Excel同时打开两个窗口的方法
  2. firefox图片不存在就显示小红叉的方法
  3. Maven实战 Item4 -- Maven核心概念
  4. vue 用echarts写的进度条组件
  5. 【推荐】git commit 规范和如何在 commit 里使用 emoji
  6. python爬虫---如何爬取京东商品评论并进行数据存储
  7. 黑马程序员2022新版python教程补充(P61)
  8. Ruby 之Gem kaminari 分页
  9. idea2019.2版本gradle 使用offline
  10. kylin2.1.0 + CHD5.7环境搭建