第一章 开始
1、return
在大多数系统中return 不管返回什么值都是结束函数,但是返回0表示正常退出,返回-1表示异常,我们在显示面板可以看到是打印出-1还是0
2、g++是编译器
3、输入输出流
向流中写入数据
向流向向流中读取数据
文件存取在流中,读取的时候是一个一个读取的,或者存放,输出完一个要记得打空格
4、关于包含头文件
<>标准头文件中查找 ,“ ”当前代码目录中查找
第二章 变量和基本类型
2.1 基本内置类型
1、数据的大小
2、有符号与无符号
无符号(unsigned)只可以表示大于等于0的数(注意不能为负数,不然会报错误)
有符号(signed)表示大于0,小于0,或者等于0的数
int、short、long、long long 都是有符号,只要在前面加上unsigned 就可以变成无符号的数据
char 无符号可以存放数值 0~256 有符号存放 -128~127
2.2 变量
1、初始化列表
初始化是指创建变量的时候赋给其一个初始值,而赋值的含义是将当前对象的值擦去赋给一个新的值
只有花括号里面才是初始化列表
int a = 0;
int a = {0}; //叫做列表初始化
int a{0};
int a(0);
默认初始化(只定义不赋给初值)
定义在函数体之外(全局变量)会被系统默认初始化为0
在函数体内部的局部变量不会进行初始化
类会自己定义该怎么默认初始化化,string类是默认一个空串
2、名字的作用域
同一个名字在不同的作用域中可能指向不同的实体,名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束
main这种函数名字,是属于全局变量,作用域是整个程
3、声明与定义
为了让我们在其他程序中的变量可以在当前程序中使用,我们在变量前面加上extern(主程序与子程序之间)
4、字面值
字面值意思是只能使用它的面值来称呼它,形如 3.14、0x24、“BEIJING”
2.3 复合类型
1、引用(绑定)(引用不是一个对象,指针才是一个对象)
引用的本质其实是指针
引用为函数变量起了另外的一个名字,这个两个名字是共用一个内存空间
int ival = 1024;
int &refval = ival; //refval 指向ival(是ival的另一个名字)
int &refval2 ;//引用必须被初始化
引用和它的初始值是一起绑定的,而不是将初始值拷贝
被引用一旦初始化完成,就不可以改变
引用将和它的初始值对象绑定在一起,无法令引用重新绑定另一个对象上去
(指针可以改变指向的对象)
为引用赋值,实际上是把值赋给了与引用绑定的对象。获取引用的值,实际上是获取了与引用对象绑定的初始值
指向指针的引用
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用
int i = 42;
int *p ; //p是一个int型指针
int *&r = p;//r是一个对指针p的引用
r = &i;//r引用了一个指针p,这样把i的地址给r意思就是指针p指向i
*r = 0;//解引用r得到i,也就是p指向的对象的值变成0
2、指针
指针的本质是一个存放空间地址的变量,指针指向空间地址的对象
指针的数据类型是表示指针指向空间存放的数据类型
int *ip1,*ip2; //ip1和ip2都是指向int对象的指针
double dp, *dp2;//dp2是指向double型对象的指针,dp是doule型对象
如果指针指向对象,则允许使用解引用(*)来访问该对象
void* 指针,可以存放任意对象的地址,但是我们不可以访问地址空间的对象
指向指针的指针
int ival = 1024:
int *pi = &ival;//pi指向一个int型的数
int **ppi = π//ppi指向一个int类型的指针
2.4 const限定符
const 定义一个变量,该变量的值不可以改变,只能在初始化的时候创建出一个值
键字 const 对变量的类型加以限定
const int bufsize = 512; //输入缓冲区大小
这样就把bufsize定义成一个变量。任何试图为bufsize赋值的都将引起错误
bufsize = 512 ;//错误:试图对const对象赋值
如果想在多个文件之间共享一个const对象,要使用extern
const 的引用
const int &r1 = ci; //正确的引用及其对象对象都是常量
指针与const
int *const curErr = &errNumber; //curErr 将一直指向errNumber
顶层const 与底层const
指针本身是一个对象,并且这个对象可以指向其他对象
所以const修饰的是指针本身还是指针指向的对象就是个问题
顶层const
表示指针本身是常量
int i = 0;
int *const p1 = &i;//不能改变p1的值,这是一个顶层的const
const int ci = 42; //不能改变ci的值,这是一个顶层const
底层const
指针所指对象是一个常量
constexpr
常量表达式是指不会改变并且在编译过程中就可以得到结果表达式,可以使用constexpr修饰
constexpr int mf = 20;
2.5 处理类型
typedef 定义数据类型的别名
将char* 定义为pstring
auto
1.用auto声明的变量必须初始化(auto是根据后面的值来推测这个变量的类型,如果后面没有值,自然会报错)
2.函数和模板参数不能被声明为auto(原因同上)
3.因为auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof和typeid
4.定义在一个auto序列的变量必须始终推导成同一类型
//由vall 和val2 相加的结果可以推断出item的类型
auto item =val1 + val2l//注意item变量要有初始值
decltype 选择并且返回操作数的数据类型
希望从表达式的类型推断出他要定义的变量的类型,但是不想用该表达式的值初始化变量
decltype(f()) sum = x;//sum的类型就时函数f的返回类型
第三章 字符串、向量、数组
3.1 命名空间的using 声明
using namespace std;
using 声明,当我们使用名字cin的时,从命名空间std中获取它,不需要使用前缀std::cin
3.2 标准库string
初始化
直接初始化与拷贝初始化(将等号右边赋给左边就是拷贝)
getline
读取一行代码(包括换行符),但是将对象读到string中的时候没有换行符
字面值对象 + 字符串对象 = 字符串对象
string s4 = s1 + ", " //字符串与字面值相加
遍历字符(c++11新标准)
for(declaration : expression)
statement
其中,express部分是一个对象,用于表示一个序列。declaration 部分负责定义一个变量,该变量将用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个值
string str("some string");//每行输出str中的字符
for(auto c : str)//str的值是c下一次的初始
cout << c << endl;
3.3 vector容器
定义和初始化vector
列表初始化vector
vector<string> v1{"a","an","the"};//列表初始化,注意是花括号
可以使用数组初始化vector,但不能使用vector初始化数组
创建指定数量的元素
还可以用vector对象容纳元素数量和所有元素的统一初始值来初始化vector对象
vector<int> ivec(10,-1);//10个元素,都被初始化为-1
vector<string> svec(10,"hi!")//10个元素都被初始化为"hi!"
区分列表初始化与元素数量
vector<int> v1(10);//v1有10个元素,初始化为0
vector<int> v2{10};//v2有一个元素,该元素的值为10
vector<int> v3(10,1);//v1有10个元素,每一个初始化为1
vector<int> v4{10,1};//v4有二个元素,元素的值分别是10和1
迭代器(看成指针)
只有少部分支持数组下标操作,我们可以利用迭代器去访问容器元素地址
begin 指向容器头部
end指向容器最后一个元素的下一个位置
迭代器类型
vector<int>::iterator it;//it能读写vector<int>的元素
string::iterator it2;//it2能读写string对象中的字符
vector<int>::const_iterator it3;//it3只能读元素,不能写元素
string::const_iterator it4;//it4只能读字符,不能写字符
3.4 数组
数组作为参数进行传递的时候,两种形式
int sum(int array[], int size)//传入数组的形式
int sum(int * array, int size)//传入数组首地址 int *
二维数组参数进行传递一维数组一样,比较常用有两种传入方式,但是区别在于必须写出列数。
数组的容量是固定的在一开始就要指定
数组的初始化与定义
unsigned cnt = 42;//不是常量表达式
constexpr unsigned sz = 42;//常量表达式
int arr[10];//含有10个整数的数组
int *parr[sz];//含有42个整型指针变量的数组
(int)*parr[sz];指向42个整形变量数组的指针
显示初始化
const unsigned sz = 3;
int ial[sz] = {0,1,2}; //含有3个元素的数组,元素的值分别是0,1,2
int a2[] = {0,1,2};//维度是3的数组
int a3[5] = {0,1,2}; //等价于 a3[] = {0,1,2,0,0}
string a4[3] = {"hi","byd"};//等价于string a4[3] = {"hi","byd",""}
不允许拷贝和赋值(数组之间)
int a[] = {0,1,2};//含有3个整数的数组
int a2[] = a ; //错误,不允许使用一个数组初始化另一个数组
a2 = a;//错误: 不能把一个数组直接赋值给另一个数组
指针和数组
数组名:
相当于一个指针常量,指向数组的首元素的指针
多维数组的引用
int (&row)[4] = ia[1]//把row绑定到第二个四元素数组上,也就是第二行
(&row)[4] 代表的是一个含有四个数据的数组的引用
第四章 表达式
4.4 赋值运算符
赋值运算符
1024 = k //错误 ,字面值是右值
i + j = k; // 算术表达式是右值
ci = k // 常量是左值
混用解引用和递增运算符
auto pbeg = v.begin();
//输出元素直至遇到第一个负数为止
while(pbeg != v.end() && *pbeg >= 0)
cout << *pbeg++ << endl;//输出当前值并将pbeg向前移动一个元素
等价于
cout << *iter <<endl;
++iter;
4.5 递增和递减运算符
int i = 0, j;
j = ++i; // j=1 i=1 先加再赋值
j = i++; // j=1 i=2 先赋值再加
//方法一
cout << *iter++ << endl;
//方法二
cout << *iter << endl;
++iter;
4.7 条件运算符
string finalgrade = (grade < 60)? "fail" : "pass"
条件部分判断成绩是否小于60,如果小于,表达式的结果是fail 否则结果是pass
4.8 位运算符
注意:不要将位与(&)和逻辑与(&&)、位或(|)和逻辑或(||)、位求反(~)和逻辑非(!)
移位运算符
被移除边界的部分就会被舍弃掉
求反
~
异或(相同为0,不同为1)
^
4.11 类型转换
隐式类型转换
int ival = 3.541 + 3; //隐式转换 int 3转换成double 3.0; ival为double 6.541 再转换成int 6
显示类型转换
static_cast
只要不包含底层const,都可以使用static_cast
将一个运算对象强制转换成double类型就能使表达式执行浮点数整除法
doule slope = static_cast<double>(j)/i;
const_cast
只能改变底层const
第五章 语句
5.1 简单语句
空语句
在某个时候,语法上需要一条语句但是逻辑不需要,可以加上一个空语句
; //空语句
5.3 switch语句
while(cin >> ch)//如果是元音字母,将其对应的计数值加上1
{
switch(ch)
{
case 'a':
++acnt;
break;
case 'e':
++ecnt;
break;
case 'i':
++icnt;
break;
case 'o':
++ocnt;
break;
case 'u':
++ucnt;
break;
}
}
5.4 迭代
do while (先执行语句,后判断条件)
5.5 跳转语句
break
负责终止离它最近的while、do while for或者switch,并且从这些语句之后的第一条语句开始执行
continue
终止最近的循环中的当前迭代并且立即开始下一次迭代。
continue语句只能出现在for、while和do while循环的内部,
goto
程序从goto 语句无条件跳转到同一函数内的另一条语句
5.6 try语句和异常处理
throw表达式
用来引发一个
try 语句块
while(cin >> iteml >> item2){
try
{
//执行添加两个Sales_item 对象的代码
// 如果添加是失败,代码抛出一个runtime_error异常
}catch(runtime_error err){
//提醒用户两个ISBN必须一致,询问是否重新输入
cout << err.what()
<< "\nTry Again? Enter y or n " <<endl;
char c;
cin >> c;
if(!cin || c =='n')
break;//跳出while循环
}
}
标准异常
c++标准库里面定义了一组类,用来报告标准库遇到的问题。这些异常类也可以在用户编写的程序中使用,它们分别定义在4个头文件中
第六章 函数
6.1 函数基础
形参列表
void f1(){ }//隐式的定义空形参列表
void f2(void){ } //显示第定义空形参列表
函数的返回类型不能是数组类型或者函数类型,但可以是指向数组或者函数的指针
局部变量:形参和函数体内部定义的变量
全局变量:函数体之外定义的对象存在与整个执行过程中
自动对象
只存在于执行期间的对象,形参是一种自动对象。函数开始的时候为形参申请存储空间,因为形参定义在函数体作用之能,所以一旦函数终止,形参也就被销毁
局部静态对象
在局部变量的前面加上static,使得局部变量的生命周期贯穿函数及之后的时间,直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它产生影响
6.2 参数传递
每次调用函数时候都会重新创建它的形参,并且传入的实参对形参进行初始化。
引用传递
当参数是引用类型,它将绑定到对应的实参上,通过修改形参修改实参
值传递
当实参的值拷贝给形参时,形参和实参是两个相互独立的对象。我们说这样的实参被值传递
指数形参(C语言用的,c++改成传引用了)
将不会改变的形参尽量定义成常量引用
数组形参
注意:传参的时候,arr[] 与 arr* 是一个意思,因为传入一个数组就会转换成传入数组首地址
数组的两个特殊性质
1、不允许拷贝数组,无法进行值传递
2、使用数组的时候会将其转换成指针,所以函数传递一个数组的时候实际上传递的是指向数组首元素的地址
虽然不能进行值传递 ,但是我们可以把形参写成类似数组的形式
//尽管形式不同,但这三个print函数时等价的
//每个函数都有一个const int类型的形参
void print(const int*);
void print (const int[]); //可以看出来,函数的意图是作用域一个数组
void print (const int[10])//这里的维度表示我们期望数组含有多少元素,实际不一定
如果我们传给print函数的一个数组,则实参自动地转换成指向数组首元素的指针
显式传递一个表示数组大小的形参
//const int ia[] 等价于 const int* ia
//size表示数组的大小,将它显式地传给函数用于控制对ia元素的访问
void print(const int ia[],size_t size){
for(size_t i = 0; i != size; ++i){
cout << ia[i] << endl;
}
}
数组的引用与引用的数组
f(int &arr[10])//将arr声明成了引用的数组
f(int (&arr)[10] ) //arr是具有10个整数数组的引用
main函数的参数列表
int main(int argc,char *argv[])
(1)C 语言规定 main 函数的参数只能有两个,还规定 argc 必须是整型变量,argv 必须是指向字符串的指针数组。
(2)argc 是命令行总的参数个数。
(3)char *argv[ ] 是指针数组,数组中的每个元素都是 char * 类型,即数组中每个元素都会指向一个字符串。
argv[0] 指向程序运行的全路径名
argv[1] 指向在DOS命令行中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串
6.3 返回类型和return语句
return 与return expression 都是退出函数
return;只能出现在void类函数
return expresssiom ;只能出现在有返回类型的函数中
6.4 函数重载
main 函数不能重载
调用重载函数
定义了一组重载函数后,我们需要以合理的实参调用它们。函数匹配是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某个关联起来,函数匹配也叫重载确定。编译器首先将调用的实参与重载集合中每一个函数的形参进行比较,然后根据比较的结过决定到底调用那个函数
默认实参
使用默认实参调用函数
string window;
window = screen(); // 等价于 screen(24,80,' ')
window = screen(66); // 等价于 screen(66,80,' ')
window = screen(66,256); // 等价于 screen(66,256,' ')
window = screen(66,256,'#'); // 等价于 screen(66,256,'# ')
winfow = screen(, ,'?') //错误:只能省略尾部的实参
6.5内联函数和constexpr函数
将函数指定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。
当初内联只是一个请求,不一定会响应
普通调用:cout << shortstring(s1,s2) << endl;
内联编译过程中,消除了shortstring函数的运行时的开销
cout << (s1.size() < s2.size() ? s1:s2) << endl;
constexpr 函数
是指能用于常量表达式的函数,定义constexpr函数的方法与其他函数类似,不过要注意
1、函数的返回值类型以及所有形参的类型都得是字面值类型,并且函数体中必须有且只有一条return 语句
constexpr int new_sz(){return 42;} //new_sz()是constexpr函数
constexpr int foo = new_sz(); //正确,foo是一个常量表达式
6.6 函数匹配
当几个重载函数的形参数量相等以及某些形参的类型可以由其他类型转换得来时,就比较麻烦
6.7 函数指针
函数指针指向的对象是函数而不是对象
函数指针就是指向代码段中函数入口地址的指针。
ret (*p)(args, ...);
其中,ret
为返回值,*p
作为一个整体,代表的是指向该函数的指针,args
为形参列表。其中p
被称为函数指针变量 。
第七章 类
7.1 定义抽象数据类型
成员函数的声明必须在类的内部,它的定义则既可以在类的内部也可以在类的外部
成员函数可以随意使用类中的其他成员而不用在意成员函数的顺序
因为编译器先编译函数声明再编译函数体
引入this指针
成员函数的参数列表中其实隐藏了一个this指针,成员函数通过zhis指针来访问对象,this指针指向调用成员函数的对象,谁调用就指向谁,this是一个常量指针
编译器会把对象的地址传给this指针
引入const
引入const作用是修改隐式this指针的类型
1、默认的情况下:his的类型是指向类类型非常量版本的常量指针;例如
Sales_data *const,不能将this绑定到一个常量对象上去,这样就不能使得常量对象调用成员函数
2、const使其成为指向常量的常量指针;
const Sales_data *const
定义非成员函数
非成员函数不属于类,一般是类的接口函数(也就是调用类对象)
// 将流中的数据放到对象中去
istream &read(istream &is,Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.unit_sold >>price;
item.revenue = price *item.units_sold;
return is;
}
// 将对象内容放到流中去
ostream &print(ostream &os,const Sales_data &item){
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue <<" " << item.avg_price();
}
构造函数
目的:初始化类对象的数据成员
构造函数没有返回值
构造函数不能被声明成const
构造函数可以有多个,和其他的重载函数差不多,这样不同的构造函数之间必须在参数数量或者参数数据类型上有所区别
7.2 访问控制与封装
定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口
定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问。private部分封装了类的实现细节定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口
使用class和struct定义类唯一的区别就是默认的访问权限不同。struct默认为public,class默认为private
7.5 构造函数在探
构造函数的初值列表
一般情况
Date(int year=1999,int month=10,int day=10)
{
_year = year;
_month = month;
_day = day;
}
//初值化列表
Date(int year=1999,int month=10,int day=10)
:_year(year)
,_month(month)
,_day(day)
{}
构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序
构造函数初始化可以在变量定义前
委托构造
c++新标准
委托构造函数使用它所属类的构造函数执行它自己的初始化过程,或者说它把它自己的一些职责委托给了其他函数
class Sales_data{
public:
Sales_data(string s,unsigned cnt ,double price) :bookNo(s),units_sold(cnt),revenue(cnt*prcie){}
//其他构造函数委托上一个构造函数
Sales_data() : Sales_data("",0,0){}
Sales_data(stringn s) :Sales_data(s,0,0) {}
}
7.6 类的静态成员
有时候类需要它的一些成员与类本身直接相关,而不是与类的各个对象相关
在成员的声明之前加上关键字static 使得其与类关联在一起
class Account{
public:
void calculate() {amount += amount *interesetRate}
static douule rate() {return interestRate;}
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
}
类的静态成员存在于任何对象之外,对象不包含任何与静态对象成员相关的数据。
使用作用域直接访问静态成员
doule r;
r = Account::rate();//使用作用域运算符访问静态成员
使用类的对象、引用或者指针来访问静态成员:
Account ac1;
Account *ac2 = &ac1;
r = ac1.rate(); //通过Account的对象或引用
r = ac2->rate(); //通过指向Account 对象的指针
成员函数不能通过作用域运算符直接使用静态成员
定义静态成员
在类外定义的时候,不能使用static
静态成员的类内部初始化