2013年11月5日星期二

Learning C++ Primer Plus chapter 1- 4

    本周开始看《c++ primer plus》,其中前半部分1-7章,主要是一些c++和c的基础,计划是花两周时间简单看下,那些已知的知识点掠过,主要看一下那些没有见过的内容,以及c++中的特殊部分,习题等主要在脑中过一下。对于有一定的难度的,实地写程序来处理。

本周主要看了1-4章,大部分都掠过了,对于那些头一次见的东西,想到的东西,记录如下,其中很多内容是c++11中新加的特性,所以在编译时要加参数-std=c++11


1.列表初始化(list-initialization)(P43,P63)
这种写法来源于对数组以及数据结构的初始化,如:int a[3] = {0,1,2};
从c++98开始,可以用于单值得赋值,如:int b = {4};
c++11开始,支持可以省略等号。当大括号中没有值得时候,变量将要被初始化为0.
例如:int a{3} 等价于 int a = 3 等价于 int a = {3};
这种写法只能用于c++11标准,所以在编译时候必须加上-std=c++11

优点:有助于防范类型转换错误
具体就是:列表初始化不允许缩窄(narrowing),即变量可能无法表示赋给它的值。
举例说:
测试代码:如果用int a = 1.1; 
那最终编译过程中只会报出一个警告信息,但是最终还是可以生成可执行文件。

main.cpp:22:11: warning: implicit conversion from 'double' to 'int' changes value from 1.1 to 1 [-Wliteral-conversion]  int a = 1.1;       ~   ^~~ 1 warning generated.


如果使用int a {1.1};
则会报错误,导致无法编译通过

main.cpp:21:11: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]   int a {1.1};           ^~~ 
main.cpp:21:11: note: override this message by inserting an explicit cast   int a {1.1};           ^~~           static_cast<int>( ) 
main.cpp:21:11: warning: implicit conversion from 'double' to 'int' changes value from 1.1 to 1 [-Wliteral-conversion]   int a {1.1};          ~^~~ 1 warning and 1 error generated.


2.打印进度;
之前一直好奇一些程序的进度是怎么打印的,比如rsync文件过程中的百分比,这些数据不是按行打印的,而是在原地变化,现在终于搞懂这个是怎么搞的了,其实很简单,就是用了转义字符\r 回车符合,其实回车和换行还是有区别的,但是以前都是知其然而不知其所以然。具体可以参考程苓峰的这篇blog:《回车和换行

代码如下:
  1 #include <iostream>
  2 #include <unistd.h>
  3 #define MAXLINE 10
  4 int main(int argc, const char * argv[])
  5 {
  6   char line[MAXLINE+1] = {};
  7   for (int i=0 ;i < MAXLINE ;i++){
  8     line[i] = '-';
  9     std::cout << "\r" << line << i*10 << "%" ;
 10     std::cout.flush();
 11     sleep(1);
 12   }
 13   std::cout << std::endl;
 14   return 0;
 15 }


3.auto声明(P66)
auto声明可以让编译器根据其初始值的类型来推断变量的类型,auto起源于c语言,但是很少使用
例如:auto a = 100 a会被编译器初始化为int类型
auto b = 1.5  b会成为double
上面的情形误用会导致错误,比如上面的a本事要初始化为double的情形,会被自动初始化为int导致不符合预期

当这个特性用于复杂类型时候,会显现其威力。可以节省不少脑细胞。
std::vector <double> scores;
auto pv = scores.begin();
此时,pv会被正确的初始化为std::vector<double>::iterator 

再举一个例子:
  1 #include <iostream>
  2 #include <unistd.h>
  3 using namespace std;
  4 int main(int argc, const char * argv[])
  5 {
  6     char a = 'a';
  7     char b = 'b';
  8     char c = 'c';
  9     char * line[3] = {&a, &b, &c};
 10     char ** p_line = line;
 11     auto pp_line = line;
 12     std::cout << *p_line[1] << std::endl;
 13     std::cout << *pp_line[2] << std::endl;
 14     return 0;
 15 }  

在这里,p_line与pp_line是一样的,用auto会省略很多麻烦
注:因此特性在c++11中添加,所以使用此特性编译时需要加参数--std=c++11


4.显示原始字符串(P88)
如果要打印出带有双引号的字符串,一般情况下只能在其中加入转义字符,c++11提供了一种方便的办法,可以直接输出原始字符串。
例如:
std::cout << R"( hello "world" "\n" .)" << std::endl;
可以直接输出字符串:hello "world" "\n” .

如果要输出的字符串中含有括号,只需在双引号与逗号之间添加任意的基本字符即可:如
std::cout << R"i( hello ( my ) "world" "\n" .)i" << std::endl;

就可以输出:hello (my) "world” "\n"

其中i可以换成除了左右括号以及斜杠之外的任意可见字符
注:因此特性在c++11中添加,所以使用此特性编译时需要加参数--std=c++11

5.程序的变量存储区(P117)

以前对程序的变量存储有过一点接触,但只是听说,看了这个之后,对于程序中变量的存储区域了解的多了一点
a>自动存储(栈)
     在程序中定义的常规变量,在函数调用时候产生,函数结束时候消亡被释放,存储于栈中,正好满足栈的特点,先进后出
b>静态存储
     待详细了解
c>动态存储(堆)
     通过new申请的空间,都是在堆上,他们申请之后除非用户主动释放以及程序结束,其生命周期将一直持续。知道用户用delete释放,或者程序结束。这种机制给予程序员对数据更大的控制权。



没有评论:

发表评论