学校只教过C和JAVA,C++是自己看的,始终还是喜欢C的风格。
对C++的很多特性始终都是一知半解,也许可以学习一些细节。
第一章 快速入门
cin(see-in)标准输入
cout(see-out)标准输出
cerr(see-err)标准错误
clog
后面3个在实现上的具体区别:
cout,cerr和clog的输出都可以重定向,比如文件,通过1> 和2>的不同来区别。
cout和clog的输出是有缓冲区的,缓冲区满或者遇到endl,cerr直接输出。
比如在代码里cout和cerr、clog,然后执行文件>1.txt,那么cout的内容到达1.txt,cerr、clog的内容到屏幕。
如果使用2>,即错误输出,那么结果相反。
#include <iostream>
using namespace std;
int main()
{
cout<<"cout"<<endl;
cerr<<"cerr"<<endl;
clog<<"clog"<<endl;
return 0;
}
运行g++ test.cpp
./a.out 那么全部输出屏幕
./a.out > 1.txt 那么屏幕输出cerr和clog,文本是cout
./a.out 2>1.txt 哪么屏幕输出cout,文本是cerr和clog
cin也是有返回值的
可以使用
int value;
while(cin>>value)……;
来输入数值,如果无效或者是EOF,则返回假。
(就这几行字,重打了好几次,QQ导致死机2次,遨游神秘退出一次)
第二章 变量和基本类型
初始化分:复制初始化和直接初始化。
int ival=1000;
int ival(1000);
string all_nines(10,‘9‘);//all_nines="9999999999"
流比printf好一点,就是string输出不用加讨厌的.c_str()了。无论是cin还是cout。
const int connum=0;定义一个常量,必须初始化,以前习惯了使用宏定义的,const只是在方法的传递参数时才会使用的,不过始终感觉不到在定义常数时,const比宏定义好哪里。
定义在文件中的变量,非const变量默认为extern,如果加上const,那么这个常量就不能在这个文件以外的地方使用的了。除非extern const int connum = 0;
枚举是四个字节长度,每个成员都是int类型,可以初始化负数。
无论是c还是c++,struct都是默认实现=号赋值的。
第三章 标准库类型
cin忽略开头的空白字符(空格换行制表符)。读取时遇到空白字符,读取终止。
要读取一行用getline,不过忽略最后一个回车。
string line;
while(getline(cin,line))
cout<<line<<endl;
cout<<"ctrl + d"<<endl;
ctrl + d结束输入。
string的方法size()返回类型是string::size_type,无符号整型。
string的变量可以直接用数组的形式来操作,包括替换。比如string str1="123"; str1[1]=a;//str1="1a3"
P89页表3-3记录了对字母判断和操作的函数。或者看这里:
http://net.pku.edu.cn/~yhf/linux_c/function/01.html#linuxc1
装换大小写
for(size_type i=0;i!=s.size();++i)s[i]=tolower(s[i]);
其实也可以这样写:
transform(s.begin(),s.end(),s.begin(),tolower);
据说用stl最大的好处是可以抛弃for循环。
vector<int> ivec(10,-1)//10个成员,每个都是-1。不加第二个参数,默认给0
vector<string> svec(10,"hi")//10个成员,每个初始化赋值hi
迭代器iterator其实就是指针,但是注意,迭代器的++或者是--,不能对传入参数里面的迭代器操作,否则就会出错。
同时还提供了一个const_iterator,作用只可取值不能赋值。
这里需要区别 const iterator,这个可以赋值但是不能++或者-- 。
vector<int>::const_iterator it = vector1.begin();
*it=5;//错误,不能赋值
const vector<int>::iterator it = vector1.begin();
*it =5;//正确
it++;//错误,不能++
迭代器可以使用+n,或者-n操作。比如:
vector<int>::iterator mid = vi.begin() + vi.size()/2;
标准库bitset类型
这个我还一直没有用过。
和vector的区别在于尖括号里的长度而不是类型。
单位是位。bitset<32>bitvec(0xffff) 就是65536 。0到15位是1,16到31位是0 。
也可以用字符串来初始化:
string str("11100111"); bitset<32>bitves(str,5,3) //从第5位取3个值,那么就是0x111 。
cout<<bitves.to_ulong()<<endl; //输出7
cout<<bitves<<endl;//输出00000000000000000000000000000111
P90是bitset的方法。
第四章 数组和指针
无论是数组还是变量,在函数外定义的都是自动初始化0 ,在函数内定义的都是随机值。
如果成员是类,无论哪里定义,自动调用默认构造函数。
char a1[]={‘c‘,‘+‘,‘+‘};//打印a1,打印出了后面的乱码,一直到有0出现。
const int *a1;//*a1不可修改。a1可修改。
int *const a1;//如果指向的不是常量,那么*a1可修改。a1不可以修改。
const int *const a1;//*a1和a1都不可以修改。
这里有个很多人都会犯的错误。
typedef string *pstr;
const pstr cstr;
那么cstr是什么类型?
const string* cstr;//错误!
string *const cstr;//正确。
注意呀。
动态数组的初始化只能使用默认值0,比如:
int *pai = new int[10]();//所有值为0,小括号里不能给值,否则编译不过。
创建0个元素的数组不合法。但是创建0个元素的动态数组合法。
int a[0];//error
int *a = new int[0];//ok
第五章 表达式
&&优先级高于||
++i与i++区别:
前面的代码风格里已经说过了,后置的++会多复制一份对象出来,增加开销。不过对基础类型没有什么区别,因为编译器会做优化,但是对复杂类型就要有额外开销了。
如果程序耗尽的内存,new可能失败,系统抛出bad_alloc的异常,而不是返回NULL!
不过其实耗尽内存依然可以new成功,直到地址空间全部用完。
delete为0的指针是合法的操作,不会报错。
强制装换:
static_cast,dynamic_cast,const_cast,reinterpret_cast
实在实在是不喜欢用,直接用C的强制转换多方便,而且也看不出什么好处来。
C的强制转换相当于reinterpret_cast。
第六章 语句
int i=0;
a[i]=b[i++];
结果是a[0]=b[0];i++;
抛出异常,是从内往外找处理语句。
都不处理则交给terminate标准库函数,一般处理是退出程序。
P189 表6-1 定义各种异常。
<exception>包括最常见的异常类。
<stdexcept>定义了几种常见异常,比如rumtime_error 使用的时候直接扔,如throw runtime_error("XXX");
<new>new失败的时候抛出bad_alloc异常,而不是返回NULL。
<type_info>定义了bad_cast
try{
……
throw runtime_error("XXX");
}
catch(runtime_error err)
{
cout<<err.what()<<endl;
}
输出XXX。
如果没有catch这个异常,显示异常类为什么,然后自动打印what内容,最后退出程序。
如果觉得每个异常都要写,就写一个也行,但是XXX就没有了。比如:
catch(exception err)
{
cout<<err.what()<<endl;
}
无论扔出什么异常,打印的结果都是St9exception
不知道有什么办法可以知道抛出的是什么异常呢?
或者:
catch(...)
{
}
什么都不处理。
原来while还可以使用多个条件,如while(i<5 , j<6){}
编译处理器里面生成的宏,在代码里可以使用。以前我一直以为是vc的东西,原因是标准c的东西。
__FILE__ 文件名
__LINE__ 该行的行号
__TIME__ 编译时间
__DATE__ 编译日期
第七章 函数
内联函数类似宏定义,在编译的时候自动替换成函数的内容,避免了写成函数的额外执行开销。
内联函数的定义需要写在头文件里。
还是喜欢宏定义。