mutable、explicit、inline
一。类型
1.内置对象是否自动初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量不进行自动初始化。
2.数组
一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另外一个数组
数组初始化:没有提供数组元素初始值,则内置数组与内置对象一样处理。
不管数组在哪里定义,如果其元素为类类型,则自动调用默认构造函数,如果没默认构造函数,则必须为数组元素提供显示初始化。
char ca[]="c++"; //维数是4,多了'/0'
3.指针
NULL定义在cstdlib头文件中
void*指针不允许操作它所指向的对象。
指针与引用的区别:引用总是指向一个对象,所以定义时必须初始化;给引用赋值修改的是引用所关联的对象的值,而指针是使其自己与另外的对象关联
a.指向const对象的指针
const double *cptr;//const限定的是double对象,而不是指针cptr
不能使用void*指针保存const对象的地址,只能用const void*
运行非const对象地址赋值给const对象的指针,这时同样不能通过指针修改其值。
b.const指针
double *const cptr
c。指向const对象的const指针
const double *const cptr;
4.动态数组
动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数初始化;如果元素师内置类型,在无初始化
string *psa=new string[10];//初始化
int *pia=new int[10];//无初始化
动态数组长度后加“()”做初始化,其元素只能初始化为元素类型的默认值,不能像数组变量一样,用初始化列表为数组元素提供不同的初值。
int *ptr=new int[10]();
动态空间释放:delete [] pia ;//方括号不能丢,表示释放数组空间而不是单个对象
int a[3][4]
int (*p)[4]=a;// 括号不能丢,表示指向含有4个int元素的数组的指针;丢了表示指针数组了
ip=&a[2]
动态创建对象和动态创建数组一样,没有显示初始化时:类类型的默认构造函数初始化,内置类型不初始化
int *pi=new int; //没有初始化
sting *ps=new string;//初始化为空
int *pi=new int();// 初始化0
内存耗尽会抛出bad_alloc的异常
注意:delete p; 后p变成了悬垂指针,p所指对象已经不存了,悬垂指针往往导致程序错误,而且很难监测出来,所以立即把指针设置为0-null是比较安全的做法。
对同一个内存区域使用两次delete也是错误的。
5.sizeof操作符
要获取指针所指向对象的大小就必须解引用
对数组sizeof返回整个数据组长度,元素个数int sz=sizeof(arr)/sizeof(*arr); // 元素个数=数组长度/元素大小
6.c++中值规定了 && 、 || 、 ?、 逗号,这几个操作符的计算顺序
如 f1()* f2() 不确定先计算f1 还是f2
7.强制类型转换--应该避免使用强制类型转换:http://www.cnblogs.com/CBDoctor/archive/2011/12/06/2277918.html
reinterpret_cast<new_type>(expression)
dynamic_cast<new_type>(expression)
static_cast<new_type>(expression)
const_cast<new_type>(expression)
8.异常与调试
标准异常定义在四个头文件中:exception头文件定义了exception类
stdexcept头文件
new头文件定义了bad_alloc类型
type_info头文件定义了bad_cast类型
调试: #ifndef NDEBUG
cerr<<"debug info,file:"<<_ _FILE_ _ <<end1;
#endif
$ cc -NDEBUG main.c 开启调试
调试时用的常量: _ _FILE_ _ ; _ _LINE_ _ ; _ _TIME_ _ 文件编译时间; _ _DATE_ _ 文件编译日期
assert宏定义在cassert头文件
二、函数与参数
c++中若是不声明为引用类型,这函数都会复制实参给形参,如数组这些不允许复制的除外
9.应该将需要修改的引用形参定义为const引用,普通的非const引用形参在使用时不太灵活。
10.vector和其他容器类型形参
从避免复制vector的角度出发,应考虑将形参声明为引用类型,事实上,c++程序员倾向于传递容器的迭代器来完成容器元素操作
void printf(vector<int>::const_iterator beg,vector<int>::const_iterator end)
{
……
}
11.数组形参
注意:数组不能直接复制和赋值
void printValue(int* a){} ;//不需要修改元素值时,用 const int*
void printValue(int[] a){}
void printValue(int[10] a){}
这三个等价,形参类型都是 int* ;编译器忽略第一维长度,所以数组长度可以不定,特别是第三个,长度不为10,编译器也不会报错
传递引用类型数组:void printvlaue(int(&arr)[10]){ }
这是编译器不会将实参转换为指针,而是传递数组的应用本身,编译器严格检查实参和形参的长度是否一致
多维数组的传递:多维数组的元素本身就是数组,除了第一维以外的所有长度都是元素类型的一部分,必须明确指明:
void printValues(int (*matrix)[10],int rowSize)
void printValues(int matrix[][10],int rowSize)
数组长度控制:
a.数组本身带结束标志,如c风格字符串,以null结束
b.传递数组第一个元素和最后一个元素的下一个位置的指针
void printValues(const int *beg,const int *end)
{
while(beg!=end)
{
count<<*beg++<<endl;
}
}
c.将数组长度作为参数显示传递
12.函数返回值
a。返回非引用类型:在调用函数的地方会将函数返回值复制给临时对象--调用函数处等号左边的变量
b。返回引用类型:没有复制返回结果,而是返回对象本身,所以千万不能返回局部变量的引用(函数执行完后会释放掉)
const string &manip(const string& s)
{
string temp=s;// temp 复制了s的内容
return temp;//错误,返回局部对象的引用
}
13.定义函数的源文件一个包含声明该函数的头文件
14.static局部对象一旦创建,在程序结束前多不会撤销,定义静态局部对象的函数结束时,它也不会撤销
15.内联函数inline 就是在每个调用点,用函数的实现来直接替换调用时的函数名,就节省了调用函数的开销。
内联函数适用于优化小的、只有几行的而且经常调用的函数。
内联函数应该在头文件中定义,这点不同于一般函数,添加修改了内联函数,所有使用该头文件的所以源文件都要重新编译
16.类型成员函数
函数原型必须在类中定义,但是函数体可以在类中也可以在类外定义
const成员函数 使隐含的this指向一个const对象,所以在const成员函数中不允许修改调用改函数的对象(this所指)
17.合成的默认构造函数
如果一个类没有显示定义任何构造函数,编译器将自动生成默认构造函数,默认构造函数对于具有类类型的成员,调用该成员的默认构造函数初始化,内置对象如何初始化依赖于对象如何定义。如果对象在全局作用域定义(即不在任何函数中)或定义为静态局部对象,则这些成员将初始化为0,如果对象在局部作用域中定义,则这些成员没有初始化。-------不会初始化内置类型的成员
所以默认构造函数一般仅适用于仅包含类类型成员的 类。
18.重载函数
形参与const形参等价仅仅适用于非引用类型形参,const引用形参的函数与有 非const引用形参的函数不同,类似的,const类型 指针形参与非const对象 指针形参 不同
19.函数指针
bool (*pf)(cosnt string &,const string &);//pf指向函数的指针
bool *pf(cosnt string &,const string &) ;//定义了一个名称为pf 返回类型是bool *的函数
-------------------------------
直接引用函数名等效于函数名上应用取地址符:
typedef bool (*pf)(cosnt string &,const string &)
pf p1=pf;
pf p2=&pf;
调用函数时,不需要解引用。
------------------------------------------------------------------
返回指向函数的指针: int (*ff(int))(int *,int);//函数 ff(int) 返回int (*)(int *,int) ----它是一个函数指针
------------------------------------------------------------------
typedef int (*PF)(int *,int); //定义一个了PF的函数指针
PF ff(int);
-------------------------------------------------------------------
允许将形参定义为函数类型;;但是函数的返回类型则必须是指向函数的指针,而不能是函数
typedef int func(int *,int );
void f1(func);
func f2(int);//error
func *f3(int);//ok
---------------------------------------------------------------
void func(int);
void (*pf1)(int)=&func;
void (*pf2)(int);
pf2=&func;
三。I/O 标准库
1.i/o对象不可以复制和赋值,所以:
vector和其他容器类型只能存储可复制的元素,所以流类型都不能存在容器里
形参或者返回值也不能为流类型,如果需要传递或者返回io对象,则必须传递或者返回指向该对象的指针或引用。对象io对象读写会改变它的状态,因此引用必须是非const的。
2.输出缓冲区管理
a。用操作符显示的刷新缓冲区:end1--加换行符并刷新缓冲区;flush刷新缓冲区不添加任何字符;ends添加null结束符,刷新输出
b。unitbuff
c。将输入和输出绑定tie:任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。
tie函数可用istream或者ostream对象调用,使用一个指向ostream对象的指针形参
ostream *old_tie=cin.tie();
cin.tie(0);//打破该流上已经存在的捆绑
cin.tie(&cerr);
cin.tie(old_tie);
3.文件流
在创建fstream对象时,调用open或者使用文件名作初始化需要传递的实参要是c风格字串,若是文件名保存在string对象可调用c_str成员获取c风格字串。
关闭流并不能改变对象的内部状态,要用clear操作才能恢复流的状态。所以要重用对象实例input1就必须
input1.close();
input1.clear();
4.stringstream对象使用
string line,word;
while(getline(cin,line))
{
istringstream stream(line);
while(stream>>word)
{..........}
}
stringstream自动在数值类型之间格式化
四。顺序容器
1.容器元素的类型约束:必须支持赋值;元素对象必须可以复制,此外还有特殊要求,如果元素类型不支持这些特殊要求,则相关的容器操作不能执行:我们可以定义该类型的容器,但不能使用某些特定的操作。
io类型不可复制或者赋值,所以不能用在容器里
2.删除容器内的一个元素
string searchValue("aaa");
list<string>::iterator iter=find(slist.begin,slist.end,searchValue);
if(iter!=slist.end())//确保不是end迭代器,
slist.erase(iter);//确保erase删除的元素存在
ereas、pop_front和pop_back函数使指向被删除的元素的所有迭代器失效,对于vector容器,指向删除点后面的元素的迭代器通常也失效。而deque容器,如果删除时不包括第一个或者最后一个元素,那么该deque容器的所有迭代器都会失效,在首尾插入都不会使如何迭代器失效,首尾删除则只会使被删除的元素的迭代器失效
赋值和assign操作使左操作数容器的所有迭代器失效。
swap操作则不会使迭代器失效,swap只是将两个容器的指向 交换了而已,迭代器原先指在哪里就还是哪里,内容没有变过,还是指向原先的容器,所以存在了不同的容器中,例如,在swap前迭代器iter指向vect2[3]中的字符串,swap,该迭代器则指向vect1[3]中字符串(这是同一个字符串,只是存储在不同的容器中)
assign先删除容器所有元素,然后再添加,所以迭代器失效
3.vectro容器的自增长
size是当前容器拥有的元素个数,而capacity获取不重新分配存储空间之前可以存储的元素总数,reserve(n)则告诉vector容器capacity至少要为n
vector和deque容器提供快速随机访问,但是在容器任何位置插入和删除比在容器尾部插入删除的开销大(deque队列两端插入和删除非常快)
list在如何位置都可以快速插入和删除,但是元素的随机访问开销大
4.三种顺序容器适配器:quene、 priority_queue和stack
http://blog.csdn.net/thefutureisour/article/details/7751846
优先队列 :设置有优先级http://www.cnblogs.com/void/archive/2012/02/01/2335224.html
http://blog.163.com/cc_memory/blog/static/140463412201351471212236/
5.单词读取
string line;
while(getline(cin,line))
{
istringstream stream(line);
string word;
while(stream>>word)
count << word<<" "
}
四。类
1.成员函数在类内部声明是必需的,而定义成员函数是可选的,在类内部定义的函数默认是inline、
2.建议使用构造函数初始化列表
有些成员必须在构造函数初始化列表中进行初始化:没有默认构造函数的类类型的成员,congst或者引用类型的成员
成员被初始化的次序就是定义(声明)成员的次序,构造函数初始化列表并不指定这些初始化执行的次序
3.隐式类型转
没够单形参的构造函数都提供了从形参类型到该类对象的隐式转化,除非有明显的理由要定义隐式转化,否则,单形参构造函数应该为explicit,避免隐式转化。
4.在c语言中,static修饰的全局变量或者函数表示只允许在本.c文件中用,其他文件中不可以;不加static其他文件可以用
在c++中
static数据成员必须在类定义体内声明,在定义体外进行外部定义(正好一次,在外部定义时不需要static)
const static 数据成员在类的定义体中初始化,但是该数据成员任然必须在类的定义体之外进行定义这时不必在指明初始值
5.父类成员函数加virtrual 实现多态
6.子类不能重载从父类继承来的函数(子类不能写一个与父类中同名,但是形参不同的函数),否则子类覆盖了从父类继承来的同名函数,即子类无法调用父类那个同名函数
7.如果析构函数没有用virtual,当子类转到父类,用delete释放父类时,只会调用父类的析构函数
8.抽象类是函数前面加virtual关键字,后加 “ = 0”
抽象类不能作为函数的返回类型
不能作为函数的参数类型
9.多继承中二义性可以通过virtual继承解决,但是c++很少用多继承
多继承基本上父类都是抽象类(用抽象类模拟接口,c++中没有接口),这样就没有二义性和多继承上的各种麻烦
10.操作符重载,后置的++、--只要在重载函数的形参位置加一个“int” 占位即可,否则是前置++、--的重载
后置的一般不返回引用,而前置的返回引用