[C/C++] C++ Primer学习笔记
记录下自己掌握不清楚的概念和用法...
Day 1
endl:具有输出换行的效果,并刷新与设备相关联的缓冲区。 注:在调试程序过程中插入的输出语句都应刷新输出流,否则可能会造成程序崩溃,将会导致程序出错位置的错误判断。 buffer(缓冲区):输出缓冲区通常必须显式刷新以强制输出缓冲区内容。默认情况下,读cin会刷新cout;当程序正常结束时,cout也被刷新。 cerr:默认情况下,输出cerr不缓冲。通常用于不是程序正常逻辑部分的错误信息或其他输出。 clog:默认情况下,写到clog时是带缓冲的。通常用于将程序执行信息写入到日志文件中。 >>, << :都返回其做操作数,从而连续读入或输出。 while(std::cin >> x):在遇到文件结束符时停止读入。 注:Windows系统下--"Ctrl+z" Unix系统下--"Ctrl+d" 在C++中,把负数赋给unsigned对象是完全合法的,其结果是该负数对该类型的取值个数求模后的值。 通用转义字符: \ooo:ooo表示3个八进制数字,这三个数字表示字符的数字值。 \xddd:十六进制转义字符,由一个反斜线符、一个x和一个或多个十六进制数字组成。 字符串字面值的连接:两个相邻的仅由空格、制表符或换行符分开的字符串字面值(或宽字符串字面值),可连接成一个新字符串字面值。 两种初始化变量的形式: (1)直接初始化(更灵活且效率更高):int ival(1024); (2)复制初始化:int ival = 1024; 在C++中初始化不是赋值!!! 内置类型变量的初始化:在函数体外定义的变量都初始化成0,在函数体内定义的内置类型变量不进行自动初始化。 C++string类型==比较:若是字符串常量比较则直接比较地址。(编译器优化:相同的常量都引用同一处内存减少内存消耗) 例:"abc" == "abc"返回true,"" == "\0" 返回false extren声明变量,已初始化的extern声明被当做定义。 约束:变量只在从其定义处开始到该声明所在的作用域结束处才可以访问。必须在使用该变量的最外层作用域里面或之前定义变量。 const对象默认为文件的局部变量,非const变量默认为extern。要使const变量能在其他文件中访问,要显式指定它为extern。 const引用:指向const对象的引用。规定将普通的引用绑定到const对象是不合法的。 非const引用只能绑定到与该引用同类型的对象。 const引用则可以绑定到不同但相关的类型的对象或绑定到右值。 用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。
Day 2
头文件用于声明而不是定义。因为头文件包含在多个原文件中,所以不应该含有变量或函数的定义。 3个例外:头文件可以定义类、值在编译时就已知的const对象和inline函数。原因是编译器需要它们的定义(不只是声明)来生成代码。 字符串字面值与string类型不是同一种类型。 getline(cin,str):返回时丢弃换行符,换行符不会存储在str中。 string::size_type类型:string.size()返回值类型是size_type。 注:不能把size的返回值赋给一个int变量,因为size_type是unsigned的,赋值过程有可能会溢出。 安全的泛型编程: ·C++程序员习惯于优先选用 != 而不是 < 来编写循环判断条件。 ·调用size成员函数而不保存它返回的值是一种良好的编程习惯。 vector<int>::const_iterator //它自身的值可以改变,但不能用来改变其所指元素的值。 const vector<int>::iterator //它自身的值不能改变,但可以改变其所指元素的值。 任何改变vector长度的操作都会使已存在的迭代器失效。例如,在调用push_back之后,就不能再信赖指向vector的迭代器的值了。 用unsigned值初始化bitset对象:若bitset类型长度大于unsigned对象位数,则其余高位置0;否则,超出bitset类型长度的高阶位将被丢弃。 用string对象初始化bitset对象:从string对象读入位集的顺序是从右向左。 bitset.size()返回值是size_t。 void*指针:可以保存任何类型对象的地址。仅支持几种有限的操作: (1)与另一指针进行比较 (2)向函数传递void*指针或从函数返回void*指针 (3)给另一void*指针赋值 不能使用void*指针保存const对象的地址,而必须使用const void*类型的指针保存const对象的地址。 允许把非const对象的地址赋给指向const对象的指针,但不能通过该指针改变它指向的非const对象的值。
Day 3
允许动态创建空数组,返回的是有效的非零指针,不能进行解引用。 string.c_str():返回的是const char* 使用数组初始化vector:vector<int> ivec(int_arr, int_arr + arr_size); //两个参数分别指数组首位置和数组末位置的后一个位置 严格说,C++中没有多维数组,通常所指的多维数组其实就是数组的数组。 除法或求模: (1)两个数都为正数:除法(/)和求模(%)的结果也为正数(或零)。 (2)两个数都为负数:除法(/)操作的结果为正数(或零);而求模(%)操作的结果为负数(或零)。 (3)只有一个数为负数:除法 --- C++除法向零取整,即 a/b = (-a)/b = a/(-b); 求模 --- 根据公式:余数 = 被除数 - 商×除数 对于位操作符,由于系统不能确保如何处理其操作数的符号位,所以强烈建议使用unsigned整形操作数。 移位操作的右操作数不可以是负数,而且必须是严格小于左操作数位数的值。否则,操作未定义。 *iter++等效于*(iter++) (*p).func()等效于p->func() 对数组做sizeof操作等效于将对其元素类型做sizeof操作的结果乘上数组元素的个数。 例:int a[5]; sizeof(a)为20; 逗号表达式的结果是其最右边表达式的值。 例:输入一个整数,0结束 while(scanf("%d",&n),n); if(ia[index++] < ia[index]):C++未定义此行为,不能确定是先计算左操作数还是右操作数。 值初始化语法必须置于类型名后面,而不是变量后。 例:int *p = new int(); /*正确*/ int x(); /*错误*/ C++保证:删除0值的指针是安全的。
Day 4
switch语句:如果没有匹配的case标号(并且也没有default标号),则程序跳出switch语句。 两个case含有相同的值会报错。 若要匹配的case在default后面:default是最后执行的,只有所有的case都不匹配时才会执行,与位置无关。 在循环中定义的变量在每次循环里都要经历创建和撤销的过程。
Day 5
每次调用函数时,都会重新创建该函数所有的形参,此时所传递的实参将会初始化对应的形参。 在C语言中,具有const形参或非const形参的函数并无区别: 例:void fcn(const int i){...} void fcn(int i){...} //两函数不能重载,会发生redefine error 如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const引用。 非const引用形参只能与完全同类型的非const对象关联。 应该将不需要修改的引用形参定义为const引用。普通的非const引用形参在使用时不太灵活。 这样的形参既不能用const对象初始化,也不能用字面值或产生右值的表达式实参初始化。 C++内置数学运算符表达式就是右值。 如:int x = 3,y = 4; fcn(x + y); //传入函数的实参是const 当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。 多维数组的传递: void f(int (*matrix)[10], int rowSize); //将matrix声明为指向含有10个int型元素的数组的指针。 void f(int matrix[][10], int rowSize); //用数组语法定义多维数组。
Day 6
函数调用的实参按位置解析,默认实参只能用来替换函数调用缺少的尾部实参。 在一个文件中,只能为一个实参指定默认实参一次。通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中。 因为,如果在函数定义的形参表中提供默认是残,那么在只有在包含该函数定义的源文件中调用该函数时,默认实参才是有效的。 编译器隐式地将在类内定义的成员函数当作内联函数。 在成员函数声明的形参表后面有const:称为常量成员函数。不允许修改类的数据成员。 函数前有const,则说明函数返回值不可更改。 const对象、指向const对象的指针或引用只能用于调用其const成员函数,否则会报错。 构造函数的初始化列表(优先使用,效率较高):在冒号和花括号之间的代码称为构造函数的初始化列表。 优势:省去了临时对象的存在。在函数体内初始化相当于在构造函数当中做赋值的操作,而初始化列表是做纯粹的初始化操作。 我们都知道,C++的赋值操作是会产生临时对象的。临时对象的出现会降低程序的效率。 特殊场景:1. 类中存在const成员,那么该成员必须在初始化列表中做初始化。 2. 类中含有其它类B作为成员,而B类禁止掉赋值操作的情况下,那么对B的对象也只能通过初始化列表来实现。 构造函数重载与重复声明: 重载: int A(int &x); 和 int A(const int &x); 重复声明:int A(int x); 和 int A(const int x); (原因:非引用形参传递的只是副本,不能区分实参是否为const) 为了确定最佳匹配,编译器将实参类型到相应形参类型的转换划分等级。转换等级以降序排列如下: (1)精确匹配(exact match)。实参与形参类型相同。 (2)通过类型提升(promotion)实现的匹配。 (3)通过标准转换(standard conversion)实现的匹配。 (4)通过类类型转换(class-type conversion)实现的匹配。 对于任意整形的实参值,int型优于short型匹配,即使short型的匹配较佳。 bool (*pf)(const string&, const string&); //声明一个指向函数的指针pf bool *pf(const string&, const string&); //声明一个函数pf(...),返回值为bool* 函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化或赋值。 允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数。