2018年8月17日 22:14:16
title: C++PrimerPlus读书笔记
typora-root-url: ..
typora-copy-images-to: ..\img\root
date: 2018-08-15 22:55:29
tags:
c++
学习
categories:
学习
C++PrimerPlus读书笔记
第一章
- C++融合了C的面向过程,添加了以类为代表的面向对象,模板支持的泛型编程.
- C是一种结构化编程的语言.强调算法.C++ 强调的是一种数据结构
- 扩展名大小写,c是标准的c,C是支持C++的C
第二章
-
end1
与\n
相比,都可以执行换行,但是会刷新输出缓冲. -
token
是代码中不可分割的元素 - 声明包含了定义声明和引用声明两种形式
- 在
cin
中的<<
是进行了运算符重载,所以他能实现各种格式输出 - linux使用命令链接函数库
g++main.c -lm
- using namespace 放在函数中,则只对函数起作用,放在函数外,则对所有函数有用
第三章
- 内置类型分为基本类型(整数和浮点数)和复合类型(数组,字符串,指针,结构)
- 以两个下划线或者一个下划线和大写字母打头的名称被保留,以及一个下划线打头的用作全局标识.P38
- short至少16位,int至少与short一样长,long至少32位,至少=int,long long 至少64位,至少=long,在limits中限制
-
变量初始化方式
- c++ 可以使用类似
int i(60);
初始化变量,这类似将int看作一个class,60为构造函数的参数? - 也可使用{},如果括号为空则为0,可以 int enum{7},也可以 int a={2}.
- c++ 可以使用类似
- 数字后面的后缀
u/U
表述无符号,l/L
标识long - cinput()可以输出char的字符
- 通用字符名以
\u
或者\U
打头,\u
后面是8个十六进制位,\U
后面是16个十六进制位,用来扩展编码, - wcha_t可以存储汉字和日文,长度由编译器确定,是一种整数类型,不能使用cin/cout,可以使用wcin,wcout.
- char16_t(u作为前缀)和char32_t(U作为前缀)也是用来标识字符
- C中一般将0解释为false
- ==const==在C和C++中有所不同,一个是作用域规则,一个是const可以声明数组长度
- 可以使用E来标识浮点数,标识10的多少次方,d.dddE+n是将小数点右移,~n是左移
- 浮点类型有float,double,long double.float至少32位,double至少48位且不少于float,long double至少=double,可以从float.h看到限制,c++在cfloat
- 使用cout.setf可以设置显示精度
- 浮点常量默认使用double存储,可以用后缀f,F指定float,用L,l指定Longdouble
- ==float只能保证6位有效精度==
- 优先级,附录D.先判断优先级分组,再判断结结合性,所以同一优先级的结合性是一样的,从左往右的结合性说明对于操作数,先运行左侧的运算符.P60
- 注意:20*5+30*6,是先计算左边的20*5还是右边的30*6主要靠编译器的实现.虽然*的结合性是从左到右,但是并不是作用在同一个操作数
- 简单的运算符重载:
- 9/5 int除法
- 9.0/5.0 double
- 9L/5L long触发
- 潜在的类型转换p64
- 赋值时的隐式转换
- 表达式中有不同的类型
- short提升到int..这个在c里面应该没有
- 不同类型,小类型转换为大类型
- 参数传递
- 强制类型转换
- 形如 (int)tepm
- c++新增 int(temp),这样int看着就是一个函数风格
- c++还有强制类型转换运算符号4种P65
- static_cast <long> (temp)
第四章
- 初始化一个数组为0,只需要显示的初始化第一个元素为{0}
- c++中有以下新的特性
- int a[4]{1,2,3,4};
- 在{}可以不写值,都为0
- 列表初始化可以禁止一些缩窄的转换 比如float到int
- 数组的替代复杂用法:模版类vector和模版类array
- cin默认读取到空白,可以使用getline和get读行,使用getline会丢弃下一个换行,get并不会,
- cin.clear
- string类的简单用法
- 允许直接相互赋值
- 允许使用+
- 支持其他编码格式的字符串
- 支持RAW的字符串,里面就没有转义了,以R开头,形如
R"(xxxxxxxxxxxxxx)"
,允许在R"
和(
加上xxx,这样之后,尾巴也要使用)
和"
加入一样的,形如R"xxx(YYYY)xxx",中间的YYY是实际的 - 可以将RAW与w_char等结合,如Ru,UR暂时放着
- 在定义变量的时候可以省略struct,与c的结构相比,允许放置成员函数,同样支持位域
//声明结构类型
struct ab{
int a;int b;
}
//c这样声明变量
struct ab tmp1;
//c++ 可以省略struct
ab tmp2;
//还有以下用法
struct structName
{
int a ; int b;
}tmpname;
当有tmpname的时候可以省略structName,这样只能用一次,这时候还可以直接初始化.
struct
{
int a ; int b;
}tmpname={0,1}
//还可以这么赋值,c99以后支持的,韦东山有讲
tmpname={.b=xxx;.a=xxx}
- 公用体支持匿名公用体,这样可以少写这个公用体的名字
#include <stdio.h>
struct person
{
char *name;
union
{
char gender;
int id;
};
int age;
};
int main(void)
{
struct person jim = {"jim", 'F', 28};
printf("jim.gender = %c, jim.id = %d\n", jim.gender, jim.id);
return 0;
}
- 如果只是为了使用枚举常量,不声明枚举变量,可以忽略枚举名称,也就是匿名枚举.c++中的枚举默认是int,但有>int的,则使用4字节long,这有个词==作用域内枚举==
- int * a, b; b是int.不是指针
- 使用new为指针分配变量
int * pt = new int;//pt是个指针
,new是从堆heap和自由存储区中分配内存,定义变量是在栈.(单片机的有点不一样) - delete释放内存
int* pt=new int;delete pt;
不能使用delete释放释放过的内存,结果未知,也不能释放定义变量的内存.delete 对应的是地址,感觉应该是那个地址包含了长度,类型的链表, 可以delete 同样地址的指针,int* pt2;pt2=pt;delete pt2;
- 数组名和指针的区别:1 数组名是常亮,不能执行运算 2. sizeof指针=4 32位系统
-
==数组名取地址==,对于一个数组
short tell[10];
,存在以下三个地址- &tell[0]=数组的地址
- tell=&tell[0]=数组的地址
- &tell,这个是一个二级指针了,他指向了tell,也就是*(&tell)=tell,指向了包含20个元素的数组
- []优先级高于*,所以我们定义一个指针指向了一个20元素的数组这么定义
short ( *p)[20]=&tell
- []优先级高于*,所以我们定义一个指针指向了一个20元素的数组这么定义
short tell[10];
cout<<tell<<endl;
cout<<&tell<<endl;
//从地址上看两者是相等的,但是&tell[0]=tell是一个2字节的内存地址,&tell是一个20字节的地址,所以tell+1是地址加2,&tell+1是加了20,
//可以这么来一个指针
short (*p) [20]=&tell;
- 结构体指针成员的获取,a->b 或者 (*a).b
- vector 模版类,使用
vector <int> vi; vector <char> ch(10);
- 模版数组array,与模版类不同,他是用栈的 ,但是也支持new和delete
array<int,5> ai;
与模版类不同,他创建的对象大小是固定值,不能是变量 - 数组,模版类,模版array都可以使用数组下标访问,诸如越界a[-2]默认情况也是允许的跨界了,如果要检查可以使用模版类/模版数组的成员函数at,形如
a.at(1)=a[1]
模版类是动态数组的替代,模版类array是静态数组的替代.模版数组支持直接复制.
第五章
- 在c中,表达式成立为1,false为0,但在C++引入bool后,关系表达式是来判断bool的,C++在需要整数的时候将true和false转换为1,0,在需要bool值的时候0为false,非0为true
- 设置显示格式:cout.setf(ios_base::boolalpha);设置为显示bool还是true
- 表达式是没有分号的,有些表达式会对内存变量等有影响,有些没有,加上;就是语句了,所以可能会有a+3;这种没有意思的语句.
- 对于在for内部的int变量,新的标准是在==内部有效==,老的标准是将定义置于for之前
for(int a; a<5;a++)
- using声明与using编译指令
Using std::cout
和Using std
- 顺序点与执行顺序的问题,在循环语句中,一个表达式结束才会进入另外一个语句,表达式结束意味其中的++等操作已经实现了其副作用,但是类似 y=(1+a++)+(6+a++),没有规定左右哪个先执行
- 对于类而言,前缀++效率更高
- 优先级浅析
*++p
前缀递增与*优先级相同,从右往左,所以先结合右 ++pt,再*(++pt) -
x=*pt++
,后缀优先级更高,所以先 pt++,但是后缀运算意味着对原来的地址操作也就是x=*pt - 逗号运算符表达式,返回右侧的值
- for允许使用类似
for int i : iarrar[10]
或者for int &i : iarrar[10]
来修改数组
- C++改变参数的值可以不使用指针,只需要将参数声明为引用
istream& cin.get(char& var);
,这么用cin.get(a)
就能改变A的值,关于引用能改变参数值,其实就是传递地址了
第六章
-
文件操作
-
cin输入P187
cin输入流的机制是:
当遇到无效字符或遇到文件结束符(不是换行符,是文件中的数据已读完)时,输入流cin就处于出错状态,即无法正常提取数据。此时对cin流的所有提取操作将终止。在 IBM PC及其兼容机中,以Ctrl + Z表示文件结束符。在UNIX和Macintosh系统中,以 Ctrl + D表示文件结束符。当输入流cin处于出错状态时,如果测试cin的值,可以发现它的值为false(假),即cin为0值。如果输入流在正常状态,cin的值为true(真),即cin为 一个非0值。可以通过测试cin的值,判断流对象是否处于正常状态和提取操作是否成功。如:
if(!cn) //if表达式判断为真时cin输入流处于出错状态,无法正常提取数据
cout<<"error";错误处理
- 重置cin以接受新的输入
- 删除错误的输入cin.clear()是用来更改cin的状态标示符的。通俗的讲就是将一个出错的“流”恢复为“好”,并设置goodbit状态位。
- 提示输入
#include<iostream> const int Asize = 5; int main() { using namespace std; int golf[Asize]; int i,sum = 0; float ave; for(i = 0;i < Asize;i++) { cout << "round # " << i + 1; while(!(cin >> golf[i])) { cin.clear(); while(cin.get() != '\n') { continue; } cout << "Please enter a number:"; } } for(i = 0;i < Asize;i++) { sum = sum + golf[i]; } ave = (float)sum / Asize; cout << ave << endl; return 0; }
- cin>> ,当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。
- cin.get 关于cin.get()和cin.get(ch)区别P159
int cin.get(); istream& cin.get(char& var); istream& get ( char* s, streamsize n ); istream& get ( char* s, streamsize n, char delim )。
- cin.get读取一个字符
cin.get或者cin.get(var)
,cin.get()从输入缓冲区读取单个字符时不忽略分隔符.cin.get()的返回值是int类型,成功:读取字符的ASCII码值,遇到文件结束符时,返回EOF,即-1,Windows下标准输入输入文件结束符为Ctrl+z,Linux为Ctrl+d。cin.get(char var)如果成功返回的是cin对象,因此可以支持链式操作,如cin.get(b).get(c)。 - cin.get读取一行,读取一行可以使用istream& get ( char* s, streamsize n )或者istream& get ( char* s, size_t n, streamsize delim )。二者的区别是前者默认以换行符结束,后者可指定结束符。n表示目标空间的大小。
- cin.get(array,20);读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。这也是cin.get()读取一行与使用getline读取一行的区别所在.cin.get(str,size);读取一行时,只能将字符串读入C风格的字符串中,即char*,但是C++的getline函数可以将字符串读入C++风格的字符串中,即string类型。鉴于getline较cin.get()的这两种优点,建议使用getline进行行的读取。
- cin.getline,cin.getline不会将结束符或者换行符残留在输入缓冲区中。
istream& getline(char* s, streamsize count); //默认以换行符结束 istream& getline(char* s, streamsize count, char delim);
-
exit()是定义在cstdlib中的,exit(EXIT_FAILURE),中间的EXIT_FAILURE是系统通信参数
-
==文件操作==
ifsream 和 ofstream,fstream
-
判断是否成功打开 file.is_open
-
file.good能够判断错误,输入正确,并且没有EOF
-
读取的时候不能超过EOP,如果遇到EOF,方法eof返true
while(inflie.good()) { infole>>value; } //还可以精简如下p197 while(infole>>value) { //do }
-
第七章
- 二维数组作为参数传递示例
int data[3][4]={{1,2,3,4},{2,2,3,4},{3,2,3,4}};
int total=sum{data,3}
//原型
int sum(int (*pt)[4],int size)//这里(*pt)要有括号,否则就是 pt[4] 然后是个int 指针,是个指针数组了
//或者
int sum(int pt[][4],int size)
- 局部指针不能返回,c++中使用new分配内存是允许的,可以在外部删除
- 结构体名字知识结构的名称,需要
&
取址,早期的c不允许直接按值传递结构只能传递地址,c++允许传值,可以用指针,也可以使用引用 - 结构体允许直接复制,里面的指针是浅复制
- 模版类,数组模版传递指针作为参数
void fill (std::array<double,3> *pa)
- 递归,在递归中,每一层的局部变量都有字节的地址存储单元.
- 函数指针,标识这是一个指针,指向了一个函数
double (*pt)(int)
,pt是个指针,指向一个函数,一般情况下声明一个函数指针,可以先写出这个函数的原型,再将名字换成(pt),在这里(*pt)
等同与函数名,可以直接使用(pt)(参数); - 指针函数,标识这是一个返回值是指针的函数
double * pt(int)
,因为括号优先级高,所以先和pt结合,说明这是函数,返回值为double *
- 在函数中,允许省略标识符,下面三个是一样的
f1(const double ar[],int n)
f2(const double [],int )
f3(const double *,int )
- 函数指针数组
const double * (*pa[3])(const*,int)={f1,f2,f3};
- pa是个数组,他的类型是*,也就是一个指向指针的数组
- 指针的类型是
const double * xxx(const double*,int)
,也就是说指针是个函数xxx(const double *,int)
,参数是(const double* ,int)
,返回值是const double *
- auto 只能用于单值初始化,不能用于初始化列表,这个不太理解 P245,应该是 auto b=a; a是已知的一个值.
///都忽略参数
//step1
//pt是个指针,指向一个函数,
double (*pt)(int)
//pt是个函数,返回一个 double*
double * pt(int)
//step2
// pt是个函数,返回值为 double *
const double * pt(const double *,int)
//pt是个指针,指向一个返回值为 double*的函数
const double * (*pt)(const double* ,int)
//step3
//[]优先级高于*,所以pt是个数组,类型为*,也就是是个指针的数组,数组里存的是指针,指向了一个函数
const double * (*pt[3]) (const double* ,int)
//step4
//构造一个指针,指向step3,简单的理解就是将pt改为(*pt1)就ok,pt1指向step3的数组
//1.pt是个指针 (*pt)
//2.指向一个数组 (*pt)[3]
//3.该数组的类型是个指针,我们定义一个数组,数组的元素类型为 int* 的话,这么定义 int* a[3];
//同上, (type)* (*pt)[3] 也就是↓
const double * (*(*pt)[3]) (double * ,int)
- const
从右往左读.... 列入const int *p 就是p is a pointer to a int which is constant...其它的都可以那么读...
第八章
- 宏在某些情况下会有问题,不能传递参数,比如参数是(i++)这样的话使用替换可能导致内部有多次i++
- 在创建引用变量中
int & b=a;
注意这里的&
不是取址,而是类型标识的一部分,应认为是指向int变量的引用 - 引用与指针的一个区别,必须在声明引用的时候初始化.更像 const指针
- 早期的编译器允许引用是个常量,或者类型不匹配,为其创造一个临时引用,创建临时变量不会改变原值(类型不匹配的话)P263
- 实参类型正确,但不是左值,比如常量
- 实参类型不正确,但可以转换,比如一个long的范围比较小,转换为int,这种情况下一般需要加个const标注以下,因为不会改变引用值
- ==引用中的匿名变量==...就是上面的临时变量
- 可使用右值引用,使用&&
- 如果不希望更改引用的值,使用const声明原型
- 当函数返回值为引用,然后 a=fun(...),这样避免fun返回值先创造一个局部变量,可以直接复制.函数返回引用实际就是返回引用变量的别名 .注意:返回的引用不应该是个局部变量,就像指针一样
- 基类的引用可以指向派生类的对象,这也就是说函数原型参数为基类,但你可以用派生类的对象
- cout.setf() 格式化输出,返回的是所有配置,所以可以用返回值保存当前的配置P273
- 关于使用指针或者引用:类使用引用,结构使用引用和指针,其他使用指针
- 默认参数的右边必须是默认参数,调用是不允许跳过,比如
fn(int a ,int b=1,int c=2)
,不能使用fn(1,,3)
,默认参数一般放在原型而不是实际定义中 - 函数重载中有些参数是不能共存的,比如 x 与 &x
- 函数模版,也就是参数的类型可以是尚未可知的,比如有一个 fn(int a) 和 fn(double a),如果内部执行步骤一致,以前我们需要写两个函数,现在可以把这个提取出来.
//下面都可以,class是老的
template <typename Anytype>//一般写作 template <typename T>
template <class Anytype>
void Swap(Antype &a, Anytype &b)
{
Anytype temp;
temp=b;
b=a;
a=temp;
}
- 函数模板支持重载,与一般的重载函数一样,特征标需要不一样
- 模板的局限性:
- 一些操作无法执行,比如如果变量是个结构,那么运算符
>
;如果是数组,那=
就不成立,等等.. - 解决办法:1运算符重载 2 具体化模板定义
- 一些操作无法执行,比如如果变量是个结构,那么运算符
- 对于一个函数名,依次是
非模板函数
>显示具体化模板
>模板函数
包含他们的重载函数 - 具体显示化格式:原型和定义以template<>打头,通过名称指出类型,显示化声明后必须要有具体的定义
//模板
template<typename T>
void Swap(T &a, T &b);
struct job
{
int a;
int b;
}
//显示化声明,这里的Swap(Job)中的job是可选的,参数里面已经有了
//可以简写 template<> void Swap(job &j1, Job & j2);
template<> void Swap(Job)(job &j1, Job & j2);
//显示化定义
template<> void Swap(Job)(job &j1, Job & j2)
{
.....
}
-
还可以在函数使用的时候创建显示实例化
cout<<Add(double)(x,m)<<endl
P288 -
具体到调用函数才会产生模板的实例,称为一个实例.C++支持
- 隐式化实例
- 显示化实例,这种实例需要这么声明
template<> void Swap(Job)(job &j1, Job & j2)
,或者template<> void Swap(job &j1, Job & j2);
,还支持函数中直接实例化例如cout<<Add(double)(x,m)<<endl
- 显示具体化是指针对特殊的一些结构,做出不同的实例化的函数
-
显示实例化话是模板的一个具体实例,因为模板生成函数一般是隐式实例化的,根据实参的类型生成函数。而显示实例化直接指定生成模板的哪一种实例。显示具体化是指模板的特殊行为,理论上模板接受不同类型的参数都会按照模板定义的那样去执行,显示具体化允许在特定的形参下重新定义函数的行为。
-
模板函数的选择有一套复杂的规则,当完全匹配后,选择那个T最简单的方式
-
模板是支持多个参数不定的
template <class T1, class T2>
-
decltype 解决一些类型未知不定的情况P295,配合auto可以声明返回值的类型,注意decltype 中如果表达式式个左值,有两个括号的是引用,一个的是值的类型
-
所谓的特征标,就是函数的参数列表,翻译想出的高大上的傻逼词语
第九章
- 多文件编译,头文件包含
- 作用域表示这个东西在文件内多大范围可见,链接性表示外部文件的共享
- 静态变量都会被先初始化,然后在初始化为其他值 bss
- cv限定符...翻译出来的新玩意..就是const和volatile
- mutable,表示即使结构或者类变量为const,但是限定为mutable的某个变量也可以被修改
- const 在c++中,会将全局变量限定为static,这么做的原因是可以将
const int a =2
这种形式放到.h中,反复包含.当然也可以强制更改这个属性,在const前加上extern .c中没有这个说法.
todo
==数组名取地址==
for里面的作用域
备忘:
- (),[]优先级高于指针
- ++高于*,&指针优先级
网友评论