C++ Primer 第五版 学习摘要(一)

1.endl操作符

std::endl

在输出流中写入换行符并且刷新与设备关联的缓冲区.调试时最好添加该语句,因为输出还可能停留在缓冲区,不能正确定位程序崩溃位置.

2. 注释界定符不能嵌套使用,如:

// Wrong
/*
* /* ... */
*
*/
// Right
// /*
// *
// *
// */

3.Windows系统中,输入文件结束符的方法: ctrl+z,然后按Enter.在Unix 系统中文件结束输入 ctrl+d.

4. 两种包含文件的方法

<>   // 包含标准库文件
""   // 包含其它头文件

5. 算术表达式中不要使用char 或 bool,因为char在有些机器上为有符号字符而另一些机器上为无符号字符,最好明确指出其类型.

6.对于无符号类型赋一个超出它表示范围的值,结果是初始值对无符号类型表示的数值总数取模后的余数,如:

unsigned char c = -1; // 实际:-1/256 = 255, c = 255
7.对带符号类型赋一个超出它范围的值,结果是未定义的,此时程序运行状态未知.

8.切勿混用带符号与无符号类型.

9.初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值是指把当前值擦除,以一个新值替代.

10.程序必须初始化每一个内置类型的变量,因为如果内置变量未被显示初始化,它的值由定义的位置决定,定义于任何函数体之外的变量被初始化为0,在函数体内部的内置类型变量将不被初始化(除非声明为static).

11.声明使得名字为程序所知,而定义负责创建与名字关联的实体.变量能且只能被定义一次,但是可以被多次声明.

extern int i;            // 声明 
int j;                   // 声明并定义
extern int pi = 3.14159; // 定义,抵消了extern的作用.

12.如果在函数体内部,试图初始化一个由extern关键字标记的变量将引发错误.

13.坚持使用一种命名规范.

14.建议: 当第一次使用变量时在定义它.

15.引用

int val = 111;
int &refval = val;  // 引用
引用 必须初始化,引用即别名.

16.初始化所有指针.

17.正确定义类型

int* p1,p2;   // p1为指针,p2为int类型
int *p1,*p2;  // 正确方法
18.面对一条比较复杂的指针或引用,从右向左阅读.

19.默认状态下,const对象仅在文件内有效,若要多个文件有效则使用extern

// 方法一
const int bufSize = 512;   // 定义
extern const int bufSize;  // 声明在其他文件可用
// 方法二
extern const int bufSize = fcn(); // 定义并初始化了一个常量,能被其他文件访问.
20.常量指针与指向常量的指针
const int temp1 = 10;
int temp2 = 10;
const int *p = &temp1; // 指向整型常量的指针
p = &j ;     // 正确
*p = 10;   // 错误
int *const q = &temp2; // 指向整型的常量指针     
q = &j;      // 错误
*q = 11;     // 正确
21. C++11标准中,constexpr表示常量表达式.

22.定义别名,理解时不能简单的将别名带入表达式,因为这会产生歧义,如:

typedef char* pstring;
const pstring ctr = 0;    // 指向char的常量指针   错误
// 替换pstring
const char *ctr = 0;      // 指向常量char的指针   正确
C++11标准中,定义别名可以使用using,如:
using weekly = int;
23.auto操作符
auto item = val1 + val2;    // 自动添加类型
24.decltype操作符
int i=10;
int *p = &i;
decltype(*p) c;   // 报错,因为得到的类型为int&非int,引用必须初始化
decltype((i)) d;  // 报错,原因同上
decltype(i)  e;   // 定义了一个未初始化的int类型

25.预处理变量无视C++语言中关于作用域的规则.

26.string 成员函数的size()返回一个无符号整型,切记不能在表达式中混用带符号与无符号数,如:

n < 0;
s.size() < n;   // 几乎肯定为true,因为n被转换为较大的正数
27. string 相加必须确保"+"两侧至少一个为string,如
string s6 = (s1 + ",") + "Hello"; // 正确
string s7 = ("Hello" + ",") + s2; //错误
28.C++版本的C标准库都类似如:   name.h => cname. 且所有函数均包含在std命名空间.

29.使用数组,一定得确保下标合法.

30.初始化vector<type>

vector<int> v1(10);       // 有10个元素
vector<int> v2{10};       // 第一个元素为10
vector<int> v3(10,1);     // 10个初始值为1的元素
vector<int> v4{10,2};     // 前两个元素分别为10,2 

31.对于string,vector<string>使用()或{} 会根据能否初始化来选择.

32.当循环体内部包含添加或删除vector<type>对象的语句时,不能使用范围for循环.

33.当vector<type>,string为空时,不能使用下标形式添加元素.

34.可以通过解引用迭代器获得迭代器所指的对象.

35.标准库中定义了关于迭代器的 ==,!= ,故而推荐for循环中使用迭代器.

36.C类型字符串

char a[] = "C++"; // 字符串末尾添加一个空字符
37.类型定义的解析遵守从内(括号)向外,从右至左的原则理解.

38.当使用auto作用于数组时,实际类型为指针.

int ia[10] = {0,1,2,3,4,5,6,7,8,9};  // ia为10个整数的数组
auto ia2(ia);                        // ia2是一个整型指针, 指向ia的第一个元素
auto ia3(&ia[0]);                    // 上式的等价形式
ia2 = 42;                            // 错误,ia2是一个指针 
39.当使用decltype作用时返回为数组.
decltype(ia)  ia4 = {0,1,1,1,1,1,1,1,1,1}; // ia4为十个元素的数组
40.在标准库iterator头文件当中包含begin与end,这两个函数用来返回数组首元素以及尾后元素的指针.

注:尾后指针不能执行解引用

41.ptrdiff_t为带符号类型,表示两指针相减的结果.

42.进行指针比较时,比较类型必须一致,且指向同一目标.

43.vector与string要求下标为无符号型,而内置类型的下标运算不是无符号型,如:

int *p = &ia[2];
int k = p[-2];     // 表示ia[0]那个元素
44.C中字符串处理函数接受的参数要求必须以空字符作为结束.

45.尝试闭着眼睛编程.

46.string存在一个成员c_str返回类C的char[]类型,但使用时最好先拷贝一份以防失效.

47.使用范围for语句访问多维数组时,最好使用&以防止auto使数组转化为指针不能通过编译.

48.重载运算符不改变优先级及结合律.

49.对于没有指明执行顺序的运算符来说,如果表达式指向并修改了对象的值,将引发错误及未定义的行为,如:

cout << i << " " << ++i << endl;    // 未定义的
50.进行比较运算时,除非比较对象是布尔类型,否则不要使用布尔字面值true与false作为运算对象,如:
int val = 11;
if(val) {}                // 当val为任意非0值时,条件为真
if(val == true) {}        // 只有当val为1时,条件才为真
if(val == 1) {}           // (推荐使用)val == 1 时,条件为真
51.C++11 规定
// (-m)/n,m/(-n)   <=>  -(m/n)
// m%(-n) <=> m%n,  (-m)%n  <=> -(m%n)
52.== 运算符优先级高于&&与||.

53.++i与i++,前置版本返回左值,后置版本则将对象原始值的副本作为右值返回.(建议使用前置版本)

54. ? : 条件运算符可以嵌套使用,遵循从右至左结合的顺序,但切勿嵌套过多,此运算符优先级较低.

55.移位运算符最好作用于无符号类型.

56.sizeof满足右结合律,可以安全的对无效指针使用.

57.尽量减少强制类型转换.

58.块不以分号作为结尾.

59.case 标签必须是整型,常量表达式,枚举.

60.case内定义的变量要求在块内定义,对其它case不可见,这是为了避免跳转语句时产生错误.(未初始化)课本p163.

61.goto语句尽量少用,且当goto跳转回到变量定义之前,意味着系统将销毁该变量然后重新创建它.

62.即使某个形参不被函数使用,也必须为它提供一个实参,函数参数传递使用引用能够避免大规模拷贝.

63.当函数参数不改变实参值时,尽量使用常量引用,因为常量引用适用参数范围更广.如:

string::size_type find_char(string &s, char c, string::size_type &occurs);
find_char("Hello World", 'o', ctr);  // 编译会发生错误,find_char()只接受普通引用.
64.函数参数为指针时,为防止越界最好使用标准库规范,即传递begin(a),end(a)两个参数.如:
void print(const int *begin, const int *end)
{
      while(begin != end)
      {
           cout << *begin++ << endl; // 输出当前元素并且指针向前移动一个位置.
      }
}
65.返回值为void的函数隐式调用return.

66.不要返回局部对象的引用或指针.

67.函数的返回值类型决定函数调用是否是左值.如

char &get_val(string &str, string::size_type ix)
{
      return str[ix];
}
int main()
{
      string s("a value");
      cout << s << endl;
      get_val(s, 0) = 'A';    // 函数返回值为引用时调用函数能够返回左值.get_val()函数返回值为非常量的引用因而可以如此修改字符.
      cout << s < endl;
      return 0;
}
68.main函数如果没有return则会最后隐式调用.

69.main函数不能重载.

70.在C++语言中,名字查找发生在类型检查之前.(内层变量名隐藏了外层的同名变量)

71.一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值.

72.constexpr 被隐式指定为内联函数.

73.inline 内联函数声明符.

74.因为this 总是指向"这个"对象,所以this是一个常量指针不允许修改this的值,要想this可以指向常量必须在函数尾部添加const.(如此就可以将this绑定到常量对象上)

75.函数返回值为引用代表返回左值.

76.一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件.

77.IO类属于不能被拷贝的类型,因此只能引用,又因为输入输出会改变IO流的值,因而使用时应该为普通引用.

78.复合类型(较复杂比如类成员还含有其它类)被默认初始化则它们的值是未定义的,故须用户创建构造函数.

79.使用struct与class定义类的唯一区别就是默认访问权限(public,private).

80.其它函数或类可以通过友元friend来访问该类的private对象.

81.声明一个类内初始值,必须以"="或花括号表示,如:

class Window_mgr{
private:
          // 默认初始值.
          std::vector<Screen> screens{Screen(24, 80, ' ')};
};
82.一个const成员函数如果以引用的形式返回*this,那么它的返回值将是常量引用.

83.友元关系不具备传递性,每个类负责自己的友元类或友元函数.

84.类内的友元声明只作用与声明的友元,未起到函数声明的作用.(只影响访问权限)

85.编译器处理完类中的全部声明之后,才会处理成员函数的定义.

86.类外部的类型名,类内部不能重复定义.

87.成员函数中的变量名,不应该隐藏同名的类内成员变量.(补救 this->XX)

88.不要隐藏外层作用域重可能被用到的变量名.(补救 ::XX)

89.如果成员是const或是引用的话,必须将其初始化,当成员属于某种类类型时,且暂时没有默认构造函数时也必须初始化.

90.如果成员是const引用,或者属于某未提供默认构造函数的类类型,必须通过构造函数初始值列表为这些成员提供初始值.

91.最好令构造函数初始值的顺序与成员声明的顺序保持一致,且避免使用成员变量初始化其它成员变量.

92.当一个构造函数调用另一个构造函数时,假如函数体内包含代码,先执行这些代码,然后控制权才会交还给上层函数体.

93.能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向该类,类类型隐式转换的规则.且编译器只允许一步步类型转换.如:

item.combine("9-999-999");             // 错误,直接两步类型转换
item.combine(string("9-999-999"));     // 显式地转换为string, 隐式地转换为Sales_data
item.combine(Sales_data("9-999-999")); // 隐式地转换为string,显式地转换为Sales_data
94.explicit 抑制构造函数定义的转换.(只能在类内使用,该函数只能以直接初始化使用,不能以拷贝形式使用),如:
string null_book = "9-999-999";
Sale_date item(null_book);   // 正确
Sale_date item = null_book; // 错误
接受一个容器参数的vector构造函数是explicit类型的.

95.constexpr 构造函数必须初始化所有的数据成员,初始值或使用constexpr构造函数,或者是一条常量表达式.(函数体一般为空),如:

class Debug{
public:
       constexpr Debug(bool b = true) : hw(b), io(b), other(b) {}
       constexpr Debug(bool h, bool i, bool o): hw(h), io(i), other(o) {}
       constexpr bool any() {return hw || io || other;}
       void set_io(bool b) {io = b;}
       void set_hw(bool b) {hw = b;}
       void set_other(bool b) {other = b;}
private:
       bool hw;        // 硬件错误,而非IO错误
       bool io;          // IO错误
       bool other;    // 其他错误
};
96.当在类外部定义成员函数时,不能重复关键字static,该关键字智能出现在类内部的声明语句.(静态类成员也相似)

97.类的静态成员不属于任何一个对象,所以它们并不在类的构造函数里初始化,定义静态数据成员的方式和在类的外部定义成员函数一样.如:

// Account.h
class Account{
public:
      void calculate(){ amount += amount * interestRate;}
private:
      static double interestRate;
      // ...
};
// Account.cpp
double Account::interestRate = initRate();  // 定义并初始化一个静态成员
98.即使一个常量静态数据成员在类内部被初始化,通常情况也该在类外部定义一下该成员.

99.静态数据成员的类型可以是它所属的类类型.也可以作为默认实参.

100.输入输出流的状态

badbit ; // 不可恢复的系统级错误
failbit  ; // 可恢复的错误
goodbit ; // 流是否发生错误
eofbit ; // 流结束标志


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值