标准输入输出对象:cin,cout,cerr,clog
键盘输入文件结束符:(Windows下)Ctrl+z;(Unix下)Ctrl+d
标准库的头文件用<>括起来,非标准库的头文件用双引号“ ”括起来
float 6位有效数字;double 和longdouble 10位有效数字;
C++中,把负值赋给unsigned对象是完全合法的,其结果是该负数对该类型的取值个数求模后的值。
初始化:复制初始化(如:inta=2;)和直接初始化(如:inta(2);)。
初始化和赋值:初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。对内置类型来说,复制初始化和直接初始化几乎没有差别;对自定义类类型来说,直接初始化要比复制初始化更灵活且效率更高。
一个类可以有多个构造函数,每个构造函的参数个数或者参数类型必须不同。
内置类型变量的初始化:内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化成0,在函数体内定义的内置类型变量不进行自动初始化。
变量的声明和定义
C++程序通常有多文件组成,为了让多个文件访问相同的变量,C++区分了声明和定义。
变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。
声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。
extern int i;//声明但是没有定义
extern int i=5;//既是声明也是定义,相当于inti=5;
在C++中,变量必须且仅能定义一次,而且在使用变量前必须定义或声明变量。
const:常量,定义时必须初始化。
在全局作用域声明的const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。
通过指定const变量为extern,就可以在整个程序中访问const对象
非const变量默认为extern。要使const变量能够在其他文件中访问,必须显式地指定它为extern
static全局变量:只在定义该变量的文件内有效。static函数:也仅在定义该函数的文件内有效。
引用
引用是别名。必须被初始化,且必须是一个对象。
当引用初始化后,只要该引用存在,他就保持绑定到初始化时指定的对象,不可将引用绑定到另一个对象。将普通的引用绑定到const对象是不合法的。
const引用可以初始化为不同类型的对象或者初始化为右值,如:
double i=5.1;
const int &aa=5;
const int &bb=aa+2;//bb=7
double i=5.1;
int&aa=5;//error
int&bb=i;// error
非const引用只能绑定到与该引用同类型的对象。const引用可以绑定到不同但相关联系的对象或绑定到右值。
枚举
枚举类型是一组常量表达式。一旦初始化,不能改变美剧成员的值。
enumopen{input,output,append};默认地,第一个枚举成员赋值为0,后面每个成员比前面大1. input=0, output=1, append=2
enumerror{err1=2,err2,err3=5,err4}; //初始化结果为err1=2,err2=3, err3=5, err4=6
每个enum都定义了一种新类型。枚举类型的对象的初始化或赋值只能通过其美剧衬衣或同一枚举类型的其他对象来进行。
errorerr= err1; //ok error err= 2;//error error err= input; //error
类类型
定义:classBase{}; //花括号后面必须跟一个分号”;”
类可以包含0个到多个private或public访问标号
class和struct唯一差别:仅仅默认的访问级别不一样(class默认private,struct默认public)。
C++是一种静态类型语言:变量和函数在使用前必须先声明。
迭代器
for(vector<int>::iteratoriter=ivec.begin();iter!=ivec.end();++iter)
{
*iter=0;
}
for(vector<int>::const_iteratoriter=ivec.begin();iter!=ivec.end();++iter)
{
cout<<*iter<<endl;
}
const_iterator对象可用于const vector或非constvector,因为不能改写其中的元素值。
const迭代器(const vector<int>::iterator),智能用来读取其指向的元素,但不能使它指向任何其他元素。
任何改变vector长度的操作都会使已存在的迭代器失效。例如,在调用push_back之后,就不能再使用push_back之前指向vector的迭代器的值了(程序会崩溃)。
标准库bitset类型
strings(“1100”); bitset<32> bit(s); bit的位模式中的第2和3的位置为1,其余位置为0.
不一定要把整个string对象都作为bitset对象的初始值,相反,可以只用某个子串作为初始值:
stringstr(“1111101000011101110111000110”);
bitset<32>bit1(str,5,4); //从str的第五个位置开始的4个bits,即0100. bit1的从3到0的二进制位置为0100
bitset<32>bit2(str,str.size()-4); //从str的str.size()-4开始到字符串末尾,即0110.bit2的从3到0的二进制位置为0110
bitset<32> bitvec;// bitvec二进制位全为0
bitvec.set();//bitvec二进制位全置1
bitvec.set(pos); //bitvec第pos位置1
bitvec.reset(); //bitvec二进制位全置0
bitvec.reset(i); //bitvec第pos位置0
bitvec[4].flip();//bitvec[4]取反,下同
bitvec.flip(4);
cout<<bitvec;//输出bitvec(这一点和输出vector容器里面的数据不一样)
指针初始化和复值操作
把int型变量赋给指针是非法的,尽管此int型变量的值可能为0。但允许把数值0或在编译时可获得0值得const量赋给指针:
intival;
int zero=0;
const int c_ival=0;
int*pi=ival;//error
pi=zero;//error
pi=c_ival;//ok
pi=0;//ok
指针初始化或赋值时必须保证类型匹配。
void*指针:可以保存任何类型对象的地址,表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型。
修改指针所指对象的值的2种方法:
string s1("helloword");
string *sp=&s1;
*sp="goodbye";//1、解引用,给指针之间赋值。即,修改指针所指对象的值
string s2="some";
sp=&s2;//2、使指针指向另外一个同类型对象。即,修改指针本身的值
指针与引用的区别:第一个区别在于,引用总是指向某个对象,定义引用没有初始化是错误的;第二个区别在于,指针是建立关联,引用是某一对象的别名。指针是间接关联绑定到对象,引用是直接关联绑定到对象。
指针与const
1、指向const对象的指针
允许把非const对象的地址赋给指向const对象的指针。所以不能保证指向const的指针所指对象的值一定不可修改(可以把指向const的指针理解为“自以为指向const的指针”)
2、const指针
const指针,即指针本身是const,相当于“引用”
3、指向const对象的const指针
既不能修改指向对象的值也不能修改指针的指向
4、指针和typedef
string s;
typedefstring *pstring;
constpstring cstr=&s;//许多人认为是const string *cstr;然而并不是。而是 string *const cstr;
等价于string *constcstr=&s;也等价于pstringconst cstr=&s;//cstr都在指向string的常指针。
声明const pstring时,const修饰的是pstring的类型,即,是一个常指针。
多维数组:
数组的数组。
解引用和后自增操作符的优先级:后自增操作符高于解引用,如:*iter++等价于*(iter++)。
const对象的动态分配内存和释放
const int *p=newconst int(1024);delete p;//后面的const可以有,也可以没有,但是必须初始化。
动态内存的管理的常见错误:
1、delete指向动态分配内存的指针失败,也叫内存泄漏
2、读写已删除的对象。需要在delete后将指针置0
3、对同一个内存空间使用两次delete表达式。
强制类型转换(static_cast、dynamic_cast、const_cast、reinterpret_cast)
static_cast:可以实现C++中内置基本数据类型之间的相互转换。如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。
dynamic_cast:(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。(2)不能用于内置的基本数据类型的强制转换。(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。(4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。
const_cast:用来添加或删除const特性,const_cast操作不能在不同的种类间转换。
reinterpret_cast:有着和C风格的强制转换同样的能力。它可以转化任何内置的数据类型为其他任何的数据类型,也可以转化任何指针类型为其他的类型。它甚至可以转化内置的数据类型为指针,无须考虑类型安全或者常量的情形。不到万不得已绝对不用。
第7章 函数
一维数组参数传递
voidfunc(int arr[]);
voidfunc(int arr[10]);
voidfunc(const int *arr);//不修改数组形参的元素
voidfunc(int &arr[10]);等价于f(int(&arr)[10]);//函数只接受含有10个int型元素的数组
多维数组参数传递
与一维数组一样,编译器忽略第一位的长度,所以最好不要把它包含在形参表内。
voidfunc(int (matrix*)[10] ,int rowSize);//将matrix声明为指向含有10个int型元素的数组指针。
int*matrix[10]; //array of 10 pointers 10个指针的数组
int(*matrix)[10]; //pointer to an array of10 ints指向一个10个int的数组
voidfunc(int matrix[][10],int rowSize);
千万不要返回局部对象的引用。当函数执行完毕时,将释放分配给局部对象的内存空间。此时,对局部对象的引用就会指向不确定的内存。如:
const string &mainip(conststring& s)
{
string ret=s;
return ret;//Wrong
}
int main()
{
string str1("hello");
const stringstr2=mainip(str1);
return 0;
}//程序崩溃,函数返回值指向对这个程序来说不再有效地内存空间
千万不要返回局部对象的指针。一旦函数结束,局部对象被释放,返回的指针就变成了指向不再存在的对象的指针。
编译器隐式地将在类内定义的成员函数当做内联函数。
重载和const形参
1、引用
Recordlookup(Account&);
Record lookup(const Account&);
const Account a(0);
Account b;
lookup(a);//call lookup(const Account&)
lookup(b);//call lookup(Account&)
如果形参是普通引用,则不能将const对象传递给这个形参。如果传递了const对象,则只有带const引用形参的版本才是该调用可行函数。如果传递得是非const对象,则上述任意一种函数都是可行的。
2、指针
对指针形参的相关处理和引用类似。注意不能基于指针本身是否为const来实现函数的重载,如:
func(int*);
func(int*const);//重新声明。此时const用于修饰指针本身,而不是修饰指针所指向的类型,这两种情况都复制了指针,指针本身是否为const并没有带来区别。
仅当形参是引用或指针的时候,形参是否为const才对重载有影响。基于指针所指对象是否为const来实现的重载,而不是基于指针本身是否为const来实现函数的重载
指向函数的指针
函数指针是指指向函数而非指向对象的指针,如:
bool(*pf)(const string &,const string &);//指向返回bool类型并带有两个const string引用形参的函数指针,其中(*pf)两侧括号是必须的。
bool*pf(const string &,conststring &);//声明一个函数pf返回一个bool类型的指针。
用typedef简化函数指针定义:typedef bool (*pf)(const string &,conststring &);//使用时可直接用pf即可。
指向函数的指针的初始化和赋值:
只能通过同类型的函数或函数指针或0值常量表达式进行初始化或赋值。
假设有函数bool lengthComepare(conststring &,const string &);对lengthComepare的任何操作都被解释为如下类型指针:bool (*)(const string &,const string &);可使用函数名对函数指针做初始化或赋值:
pfpf1=0;
pfpf2=lengthComepare;//相当于取函数的地址
pf1=pf2;
返回指向函数的指针:int(*func(int)) (int*,int);//阅读函数指针声明的最佳方法是从声明的名字开始由里向外理解。
ff(int),将ff声明为一个带有int型参数。该函数返回int (*)(int*,int);它是一个指向函数的指针,所指向的函数返回int型并带有int*和int型的参数。等同于使用typedef的定义:
typedefint (*PF) (int*,int);
PFff(int);
允许将形参定义为函数类型,但函数的返回类型必须是指向函数的指针,而不能是函数。
标准IO库
IO对象不可复制或赋值。所以也不能存储在vector等容器中。形参或返回类型也不能为流类型。如:
ofstreamout1,out2;
out1=out2;//error
ofstreamprint(ofstream);//error
out2=print(out2);//error
流必须处于无错误状态,才能拥有输入输出。intival ;if(cin) cin>>ival;
文件模式
string ifile("test.txt");
ifstream infile;
infile.open(ifile.c_str(),ifstream::in);//默认情况下是in模式
infile.close();
ofstream outfile;
outfile.open("test.txt",ofstream::out|ofstream::app);//默认情况下是out模式
outfile.close();
fstream file;
file.open(ifile.c_str(),fstream::in|fstream::out);//默认情况下是 in | out 模式
file.close();
有效地文件模式组合:
out 打开文件做写操作,删除文件中已有的数据
out |app 打开文件做写操作,在文件尾写入
out | trunk 与out模式相同
in 打开文件做读操作
in | out 打开文件做读、写操作,并定位于文件开头处
in | out| trunk 打开文件做读、写操作,删除文件中已有的数据
上述组合中可以加上ate模式,在第一次读或写前,将文件定位于文件末尾处。