C++ Primer Notes

来源:互联网 发布:在线支付网站源码 编辑:程序博客网 时间:2024/06/13 03:15

第二章

2.1 基本内置类型

  1. 明确知道变量不可能为负数时,选用无符号
  2. 整形变量一般选用int, 长一点的选用long long.浮点型变量选用double,与float相比,精度更高并且计算代价相差不大。
  3. char 和 bool不要应用于算术表达式,一些机器上char是无符号,一些是有符号,所以如果需要使用一个不大的整数,指定unsigned char或者signed char.
  4. unsigned 和 signed 不要一起用,最后的结果会转换为无符号,如果结果为负,坏菜了。

2.2变量

1.理解初始化和赋值的区别,初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。个人理解就是初始化之前的对象是没有值的。为了避免程序的未知错误,建议初始化每一个变量值,我知道string是不用初始化的。

2.3复合类型

指针与引用的区别:指针是一个对象,引用不是对象,只是对象的另一个名字。引用必须初始化并且始终绑定一个对象。&和*放在等式左边意味着变量类型,起修饰作用,放在等式右边意味着取地址和解引用(取值)。
两种定义指针的方法:二者没有对错之分,熟悉哪个就坚持用哪个。

1 int *p1, *p2;2 int* p1;  int* p2;

2.4 const限定符

const变量的值不能被改变,所以const对象必须初始化。如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。常量表达式是指值不会改变并且编译过程就能得到计算结果的表达式。如果认定一个变量是常量表达式,那就把它声明成constexpr类型以便由编译器来验证变量的值是否为常量表达式。值得注意的是:constexpr int *q=nullptr;//q是一个指向整数的常量指针 相比于 const int *q = nullptr;//q指向一个整形常量

const指针const double pi = 3.14;double *ptr = π     //错误:ptr不是常量指针,有可能会改变pi的值,而pi的值是不能被改变的const double *ptr = π //正确const double *const p = π 秘诀在于从右向左看,p代表变量名,const代表p为常量,*代表p是指针,double代表指针指向double类型的对象,const修饰double,代表指向的double类型的对象是常量。

2.5处理类型

1. typedeftypedef double wages;    //wages等同于doubletypedef wages base,*p;   //p是指向double的指针,注意等式左边的*起修饰作用const p ptr = 0;         //意味着ptr也是指向double的指针。注意不能替换为const double* ptr = 0;前者含义是常量指针, 后者是指向double常量的指针;2. autoauto定义的变量必须有初始值,并且同一语句中定义的类型要相同,注意const intint视为不同的类型。

2.6自定义数据结构

定义自己的头文件:
为了确保各个源文件中类的定义一致,类通常被定义在头文件中。头文件通常包含那些只能被定义一次的实体,如类,const, constexpr.头文件也经常用到其他头文件的功能。
预处理器概述:
确保头文件多次包含仍能安全工作的常用技术是预处理器。预处理器是编译之前执行的一段程序。其中一项功能是#include ,当预处理器遇到它,会用指定的头文件内容代替#include.还有一项预处理功能是头文件保护符,#define指令把一个名字设置为预处理变量,而它有两种状态,已定义和未定义。#ifdef只有当变量已定义为真,#ifndef只有当变量未定义时为真。一旦检查结果为真,执行后续操作直到遇到#endif指令为止。使用这些功能能有效避免重复包含。

#ifndef SALES_DATA_H #define SALES_DATA_H#include <string>struct Sales_data{    std::string bookNo;    unsigned unit_sold = 0;    double revenue = 0.0;};  //注意类后的分号#endif/*说明:第一次包含Sales_data.h时,执行操作致#endif,Sales_data.h也会被拷贝到程序中来;第二次包含时,由于SALES_DATA_H变为已定义,编译器将忽略#idndef至#endif之间的部分。整个程序中的预处理变量(包括头文件保护符,例如SALES_DATA_H)必须唯一,通常的做法是基于头文件中类的名字来构建保护符的名字,为了避免与程序中其它实体发生名字冲突,一般把预处理变量的名字全部大写。值得注意的是,即使头文件目前还没被包含在任何其他头文件中,也应该设置保护符。*/

第三章 字符串、向量、数组

3.1 命名空间的using声明

头文件不应包括using声明,因为include会将头文件拷贝到源文件,可能会造成冲突;

3.2 string

  1. string::size_type 是一种描述string对象大小的类型,显然是无符号的,所有的size()函数返回此类型。如有size_type就不要再用int,无符号和有符号一起的表达式时,有符号会变为无符号,结果会不准。
  2. 两个string对象的比较: ==、!=、<、>、+(+号两边的对象有一个需要时string,”hello”并不是string对象)
  3. cctpe中的函数: isalnum(c)-当c是字母或数字为真;类似的是大小写字母、数字、控制字符、标点符号;c++程序中include cname意味着是c++的头文件,name.h意味着是c的头文件,尽量用前者;
  4. string s(lee song yu);for (auto &c :s) c = toupper(c);//c是字符的引用,才能改变s;toupper为将字符变成大写。 //注意: for (a:all) 是范围for循环

3.3 vector

  1. vector的初始化: vector v(10, 42); //代表着有10个值,每个值是42; 列表初始化vector v= {1,2,3,4,5} or vector v{1,2,3,4,5};值得一提的是,vector可以高效的动态添加,所以一开始不指定初始值是个很好的选择。除非初始值都相同。
  2. 范围for语句体内不能改变其所遍历序列的大小
    vector<int> v(10,1); for(auto i:v) v.push_back(1); //错误

3.4 迭代器

  1. 容器为空,v.begin()和v.end()返回的是同一值,end也被称为尾后迭代器,所以可以用v.begin()==v.end()判断容器是否为空。
  2. 迭代器两种类型,iterator、const_iterator,后者及不能改变容器的值。c++11标准cbegin(),cend()返回的迭代器类型就是后者。但一般可以直接指定 auto i = v.begin();
  3. *iter返回的是iter所指元素的引用;箭头运算符简化了it->mem 和(*it).mem等价
  4. 凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
  5. 迭代器运算,不能超过end,+,-,<,>.

3.5 数组

  1. 初始化必须指定大小,即元素个数必须为常量表达式;例如 unsigned num =10;int a[num];//错误,num不是常量表达式;或者用列表初始化法:int a[]={1,2,3,4,5}; 不能用一个数组去初始化另一个数组,对比的是vector可以,vector<int> v(v1);char类型的数组可以用 char c[]="song";来初始化,但此时字符串末尾的空字符也被复制到数组c里,也就是说数组c的大小事5;
  2. c++11新标准,int a[]={1,2,3,4,5}; int *p = begin(a); int *p1 = end(a);//p代表指向数组第一个元素的指针,p1代表指向数组最后一个位置的下一个位置的指针。与迭代器中begin和end用法相似。 使用数组初始化vector, vector<int> v(begin(a), end(a));//只需指明拷贝区域的首元素地址和尾后地址 vector<int> v(a+1, a+4); //指针运算不要超过数组的最后一个位置的下一个位置
  3. int *p[10];//含有10个整形指针的数组 int (*p)[10];//p指向一个含有10个整形变量的数组 int(&a)[10];//a是一个含有10个整形变量数组的引用
  4. 内置的下标运算符的索引值不是无符号类型,与vector和string不同,即内标访问可以为负。
  5. 尽量不用C风格字符串,代为使用string,安全、高效。

3.6 多维数组

  1. int a[3][4]; 第一个数字代表数组大小,第二个数字代表元素大小;这好似一堵墙;二维数组称为行、列。 int arr[2][3][4]; 这好比一个立方体;
  2. int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; 初始化值。 int a[3][4] = {{0},{4},{8}}; 初始化每行的第一个元素; int a[3][4] = {0,1,2,3};初始化第一行。
  3. 范围for语句处理多维数组int ia[rowCnt][colCnt]; size_t cnt =0; for(auto &row : ia) for(auto &col : row =) col = cnt; cnt++;// 值得注意的是,用引用不仅是因为要修改数组元素的值,更是为了避免数组自动转化为指针;例如 for(auto row:ia) 此时row退化成指向该数组首元素的指针。所以,利用范围for语句处理多维数组,除了最内层的嵌套,其它都应该是引用。
  4. auto 来访问多维数组;
方式1int ia[3][4];for(auto p=ia;p!=ia+3;++p){    //ia退化成指向数组首元素的指针,p指向一个含有4个整形元素的数组    for(auto q = *p;q!=*p+4;++q)      //*p是一个含有4个整数的数组(*p代表p所指向的东西),*p退化成指向数组的首元素的指针       cout<<*q<<' ';    cout<<endl;}方式2for(auto p=begin(ia);p!=end(ia);++p){    for(auto q = begin(*p);q!=end(*p);++q)        cout<<*q<<' ';    cout<<endl;}

第四章 表达式

  1. -m%n = -(m%n) ; m%(-n) = m%n;
  2. 短路求值: 逻辑与&& 逻辑或, 左侧运算对象是为了确保右侧对象求值过程的正确性和安全性。
  3. 赋值满足右结合律: a = b =0; b=0, a =b;
  4. 赋值运算符的优先级低于关系运算符,在条件语句中,赋值部分通常加上括号;if((i=v.getvalue())!=42)
  5. 除非必须,否则不用递增的后置版本,因为后置版本还要再返回原始值,建议养成前置版本的习惯。如果想在复合表达式中既将变量加1减1又能使用它原来的值,就可以用后置版本。
auto p = v.begin();while(p!=v.end()&& *p>=0)   cout<<*p++<<endl;         //++的优先级高于解引用,所以等价于*(p++),先运行后置,将p的值加1,但返回的是p的原始值。
  1. p->mem 等价于 (*p).mem
string s = "song", *p = &s;auto n =(*p).size();   //二者等价于s.size()n = p->size();
  1. 条件运算符的优先级很低,当长表达式内嵌套了条件运算符,在两边加上括号。还可以嵌套条件运算符
string final = (grade>90)?"high pass":(grade<60)?"fail":"pass";
  1. 位运算符作用于整形,如果运算对象是小整形,它的值会自动被提升。char类型进行位运算会被提升为int类型。关于符号位没有明确的定义,所以强烈建议将位运算作用于无符号类型。
  2. 对数组执行sizeof运算得到整个数组所占空间的大小,所以可用sizeof(ia)/sizeof(*a)求数组大小。
  3. 逗号运算符,表达式1,表达式2.先求解表达式1,再求解表达式2,最后返回表达式2的值。常用于for循环。将size到1的值赋给v, 其实也就是并列执行。逗号的优先级最低。
vector<int>::size_type cnt = v.size();for(vector<int>::size_type i =0;i!=cnt;++ix,--cnt)   v[i] =cnt;
  1. 类型转换, double fval; bool flag; flag = fval; 如果fval为0,flag为false,其他的值为true.