您的位置:首页 > 编程语言 > C语言/C++

C++ Primer阅读心得(第三章)

2008-01-13 01:23 225 查看
1.在头文件(*.h或者*.hpp)中不要使用using语句。因为如果头文件中使用了using语句后,每个include它的源文件中都默认包含了这个using,那么可能会与这个源文件中的变量名或者其他的头文件中的变量名产生冲突。

2.string对象的读入:重载的>>以空格为结束符,而getline以换行符为结束符。

string s;
cin>>s;
cout<<s;
输入:“Hello World!”
输出:“Hello”

string s;
getline(cin, s);
cout<<s;
输入:“   Hello World!”
输出:“   Hello World!”(注意,连字符之前的空格都读入了)


3.c++11中修正了>号在声明STL对象时的bug。(这bug终于修上了啊~~泪奔)

vector<vector<int> > ivv; //旧版,两个>之间必须有空格,否则编译错误
vector<vector<int>> ivv;  //c++11版,不需要在两个>之间加入空格
4.列表初始化vector时需要注意编译器对列表{}中内容的解释顺序:

优先按照列表初始化处理,将{}中的每个值作为vector中的元素填入
如果{}中的值不能全部解释为vector的元素,则尝试调用其他的构造函数
vector<string> v1 = {"a", "an", "the"}; //ok,列表初始化
vector<string> v2{"a", "an", "the"}; //ok,列表初始化
vector<string> v3{10}; //ok,虽然无法使用10列表初始化,但是还可以调用构造函数创建一个大小为10的string vector

5.注意string对象和vector的size()函数返回的是size_type类型,这是个与机器无关的无符号类型(用int型接着小心溢出...),嫌string::size_type或者vector<type>::size_type写起来麻烦的可以使用auto或者decltype来偷懒。

6.使用STL的泛型编程时应该注意,使用迭代器遍历一个序列时,循环中判断是否越界要使用!=,而不要使用<或者>。这是因为所有的标准库容器都定义了==和!=,而只有少数的容器定义了<和>。

for(auto it = s.begin(); it!=s.end(); it++)
//do sth
7. const_iterator:在容器中,存在一个const_iterator的迭代器,对这个跌迭代器使用*号(解引用)将返回一个const对象。也就说使用const_iterator只能只读的访问容器中的数据,但是const_iterator本身可以修改。注意:const对象的begin()和end()会返回const_iterator,而非const对象的begin()和end()只会返回普通iterator。另外,在c++11中,为了使用方便,定义了两个新的函数cbegin()和cend(),无论对象是否const都返回const_iterator。
string sa = "abc";
const string sb = "def";
auto it1 = sa.begin(); //普通iterator
auto it2 = sb.begin(); //const_iterator
auto it3 = sa.cbegin(); //const_iterator


8.difference_type:我们知道,对容器调用size()方法会返回一个size_type类型的整数,相当于unsigned。在此之外,如果同一个容器的两个iterator相减,将返回difference_type类型的整数,相当于signed,嗯,当然也是表示范围很大的一个类型。

9.数组的维数与constexpr:在定义数组时,数组的维数要求是编译器就能确定的大于1的整数。除了整数字面常量及表达式、枚举成员、使用整数字面常量或表达式初始化的const整型对象之外,还可以使用c++11中新定义的constexpr声明的变量或者函数。

10.声明数组时要求有明确的类型,所以不能使用auto来作数组的类型。数组的元素应该是对象,所以不允许引用数组。

11.可以使用空{}列表初始化内置类型数组为默认值(可以少写一个memset了?):

int a[16] = {};        //全是0
double b[16] = {}; //全是0
char c[16] = {};     //全是'\0'


12.指针数组、指向数组的指针和指向数组的引用(不是引用的数组呦~~)

int *pa[10]; //int *的指针数组
int (*pa)[10]; //指向有10个元素的数组的指针
int (&pa)[10]; //指向有10个元素的数组的引用
//可以按照离变量名的远近关系依次解读来搞清楚这些定义:(后续的函数指针也可以同样解读)
//例如:int (&pa)[10];
// 1.pa是一个引用
// 2.pa是一个有10个元素的int数组的引用
13.数组维数和下标的正确类型是size_t。(我一直用int的...OMG!)size_t是一个与机器相关的unsigned整数类型,足以存下数组所有的下标。

14.数组头指针与auto和decltype:我们知道,数组的名称会被编译器自动解析为数组的头指针(数组的第一个元素的指针)。当使用auto和数组名时,你会得到指针类型;当使用decltype和数组名时,你会得到数组类型(果然auto比decltype多做了一层转换):

int ia[] = {1,2,3,4,5};
auto a = ia; //a的类型是int *
decltype(ia) b; //b的类型是int [5]
15.为了指针(数组)的使用安全与方便,c++11新增了begin()和end()库函数,它们会产生和STL容器的begin()和end()同样的行为(一个指向开头,另一个指向末尾元素的下一个位置):
int ia[2][3] = {1,2,3,4,5,6};
for(auto p=begin(ia); p!=end(ia); p++)
for(auto q=begin(*p); q!=end(*p); q++)
//do sth
16.两个指针做差,返回的类型是ptrdiff_t类型,属于signed,类似type_size和difference_size的关系,正好与size_t组成一对。

17.STL容器的下标要求必须是非负整数,而数组的下标没有这一要求,也就是说,你可以使用负数。(我的三观啊啊啊~~)

int a[10];
int *p = a + 5; //p指向a[6]
int k = p[-2];     //k = a[4]
18.c++语言中没有多维数组,只有数组的数组。
int ia[3][4]; //ia是一个大小为3的数组,其中每个元素都是大小为4的int型数组
19.范围for与多维数组:通常我们使用下标和多重循环遍历多维数组,但是当我们使用c++11中新增加的范围for的时候,必须声明每一层循环时的对象类型。通过第14项我们可以知道,使用auto时数组名会被自动解析为指针类型,这会导致内层循环无法进行下去(指针不是序列,无法应用范围for)。这时我们需要将除了最内层循环之外的每一层循环的对象类型声明为auto &,就可以避免数组名会被自动解析为指针类型,让内层循环正确运行。
int matrix[2][3] = {1,2,3,4,5,6};
for (auto row : matrix)
for (auto element : row) //编译错误,row是指针无法范围for
//do sth
//改为:
for (auto &row : matrix)
for (auto element : row) //ok,row是int [3]
//do sth
20.多维数组的数组名会被转化为第一层数组首元素的指针(这是15和19加在一起的推论),我们可以使用auto来简化它:
int ia[2][3][4];
int (*p)[3][4] = ia; //转化首元素指针
auto *p = ia;         //方便了,但是容易记不清类型
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息