这是一系列关于C++ Primer的读书笔记,很简单,每一篇记录20条我觉得有趣的,有用的,难懂的地方。
- 在Window中,输入文件结束符的方法是敲Ctrl+Z,然后按Enter或Return键,在UNIX系统中则是Ctrl+D。
- 包含来自标准库的头文件时,也应该用尖括号(<>)包围头文件名。对于不属于标准库的头文件,则用双引号(””)包围。
- 切勿混用带符号类型和无符号类型。
a是int,b是unsigned,则结果视在当前机器上int所占位数而定,在32位的环境中,结果是4294967295。(P35) - 严格来说,十进制字面值不会是负数。如果我们使用了一个形如-42的负十进制字面值,那个负号并不在字面值之内,它的作用仅仅是对字面值取负值而已。
- 使用类型修饰符的时候定义多个变量,会有两种不同的写法。如下,这两种定义指针或引用的不同方法没有孰对孰错之分,关键是选择并坚持其中的一种写法,不要总是变来变去。
12int* p;int *p;
当使用后者的时候应当注意每条语句只定义一个变量,否则可能会造成错误:
1int* p1.p2; - 当存在有指向指针的引用的时候,如下:
123456int i = 42;int *p;int *&r = p;r = &i;*r = 0;
只需要从右向左阅读r的定义,离变量名最近的符号对变量的类型有最直接的影响,因此r是一个引用。声明符的其他部分用以确定r引用的类型是什么。 - 指向常量的指针和常量引用一样,没有规定其所指的对象必须是一个常量,所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。(P56)
- 指向常量的指针(pointer to const)和常量指针(const pointer)。
指针是一个对象,本身允许被定义为常量,被称为常量指针。常量指针必须初始化,一旦初始化完成,则它的值(即存放在指针中的那个地址)就不能再改变了。
12345int errNumb = 0;const int *perrNum = &errNumb; //perrNum指向的对象errNumb的值不能改变。int *const curErr = &errNumb; //curErr将一直指向errNumb.const double pi = 3.1415926;const double *const π //pip是一个指向常量对象的常量指针。
(P56) - 在cctype头文件中定义了一组标准库函数处理特定字符。其中主要的函数名及其含义如下:
另外,建议在C++程序中使用形如cctype而不是ctype.h这种形式的头文件。 - C++11新添加了一种语法糖:范围for(range for)。
其语法形式为:for (declaration : expression)
statement比如计数一个字符串中数字的个数,代码为:
12345678string str = "abc123d4e56fg";decltype(str.size()) count = 0;for (auto c : str){if (isdigit(c))++count;}cout << count << endl;
其中decltype是c++11中的一个新特性,也是一个新的关键字,表示获取括号里面的类型作为类型说明符。
如果在使用范围for的时候想改变这个字符串的内容,应该使用引用来声明临时变量,如下面的代码,将一个字符串中所有字符改为大写:
123456string str = "abcdefABCDEF";for (auto& c : str){c = toupper(c);}cout << str << endl; - 标准库中有两个函数分别为begin()和end(),可以输入一个数组,返回指向该数组首元素或是尾元素的指针。可以用这两个函数将一个C数组方便地拷贝到一个vector对象中,代码如下:
12int int_arr[] = {0, 1, 2, 3, 4, 5};vector<int> iVec(begin(int_arr), end(int_arr)); - (P237) C++11中对于默认构造函数有一种新的实现方式:
1234class A {int x;A() = default;}
不过需要注意的是,这种方法仅限于我们为成员数据提供了初始值的时候才有用。如果编译器不支持自动为内置类型初始化的话,就不能这样使用。 - (P240) class和struct都可以作为类的声明符,它们两个的唯一区别仅仅在于默认的访问权限不同,在指定访问说明符public和private之前所声明的成员,在struct中都是public的,而在class中则恰好相反,都是private的。
- (P245) 可变数据成员(mutable data member),在一个const成员函数中,我们是不能修改这个类的任何成员函数的,不过有的时候入如果我们想指定某一个成员不论是否在const成员函数中我们都可以去修改它,我们可以在变量的声明中加入mutable关键字做到这一点:
12345678910class Screen {public:void f() const;private:mutable int a;}void Screen::f() const {++a;}
即便在函数f()中,我们也可以修改成员变量a。 - (P261) 委托构造函数(delegating constructor),委托函数可以用于减轻程序员维护构造函数的重复性工作。一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程:
1234567891011class C {public:C(int x, string y) : a(x), b(y) {}//其余构造函数全部委托给第一个构造函数C() : C(0, "") {}C(int x) : C(x, "") {}C(string y) : C(0, y) {}private:int a;string b;} - (P286)文件模式(file mode),用来指出如何使用文件。
in: 以读的方式打开,只可以对ifstream或者fstream使用
out: 以写的方式打开,只可以对ofstream或者fstream使用
app: 每次写操作前均定位到文件末尾
ate: 打开文件后立即定位到文件末尾
trunc: 截断文件
binary: 以二进制方式进行IO
用法:
1ofstream("file", ofstream::out | ofstream::trunc); - (P288) 使用istringstream和ostringstrem这两个流对象来模拟c中的sprintf()和sscanf()。
- (P292) 标准库中的顺序容器有:vector、deque、list、forward_list、array、string。
其中forward_list和array是新标准新加的,前者设计用来达到与最好的手写的单项链表数据结构有相当的性能,需要注意的是forward_list并没有size()操作,而别的容器的size()操作保证是一个时间常量操作。
另外,如果不确定应该使用哪种容器,可以在程序中只使用vector和list公共的操作:迭代器,而不使用下标操作,避免随机访问,这样在必要选择使用vector和list的时候都很方便。 - (P299) STL容器定义和初始化的六种方法:
1234567891011121314151617181920//方法1,默认的构造函数,如果c是一个array,则c中元素按默认方式初始化,否则为空C c;//方法2,c1初始化为c2的拷贝。c1和c2必须是相同类型,若是array的话大小也必须相同C c1(c2);C c1=c2;//方法3,使用初始化列表来初始化容器,对于array来说,初始化列表元素个数必须小于或者等于array大小,对于遗漏的元素则按默认值初始化。C c{a, b, c...};C c={a, b, c...};//方法4,初始化为迭代器b和e指定范围中的元素的拷贝(不适用于array)C c(b,e);//只有顺序容器才能接受大小参数//方法5,定义seq包含有n个元素,这些元素进行了值初始化C seq(n);//方法6,seq包含n个初始化值为t的元素C seq(n, t); - (P302) assign和swap两个操作函数,可以用来赋值和交换容器里面的数据。
其中,assign接受两个迭代器,将迭代器指代范围内的数据赋值给调用它的对象:
123list<string> names;vector<const char*> oldstyle;names.assign(oldstyle.cbegin(), oldstyle.cend());
swap可以操作交换两个相同类型容器的内容,swap只是交换了两个容器的内部数据结构,元素本身并未交换,因为这个操作很快,在常数时间内。(除了array之外,使用swap交换array是真正地交换他们的元素,因此所需的时间与array中元素的数目成正比)
123vector<string> svec1(10);vector<string> svec2(24);swap(svec1, svec2);