数组:
1、如果没有显示提供元素初值,则数组回想普通变量一样初始化:
(1)在函数体外定义的内置数组,其元素均初始化为0
(2)在函数体内定义的内置数组,其元素无初始化。
(3)不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类
没有默认构造函数,则必须为该数组的元素提供显示初始化。
2、现代c++程序采用vector类型和迭代器取代一般的数组、采用string类型取代c风格字符串。
指针:
1、c++提供了一种特殊的指针类型void*,它可以保存任何类型对象的地址,void*表明该指针与一地址值相关,
但不清楚存储在此地址上的对象的类型。
2、虽然使用引用和指针都可间接访问另一个值,但他们有两个重要的区别:
(1)引用总是指向某个对象:定义引用时没有初始化是错误的;
(2)给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。
引用一经初始化,就始终指向同一个特定对象(这就是为什么引用必须在定义是初始化的原因)
3、c++语言强制要求指向const对象的指针也必须具有const特性:const double *cpstr;
cptr本身并不是const,在定义时不需要对它进行初始化,如果需要的话允许给cptr重新赋值,使其指向另一个
const对象,但不能通过cptr修改其所指对象的值。把指向const对象的指针理解为“自以为指向const的指针”。
4、int *const curErr=&errNumb;读作"curErr是指向int 型对象的const指针",const指针的值不能修改,即不能
使curErr指向其他对象。
5、指向const对象的const指针:const double *const pi_ptr=π既不能修改pi_ptr所指向对象的值,也不允许
修改该指针的指向;读作”pi_ptr首先是一个const指针,指向double雷翔的const对象“
6、创建动态数组:每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为
程序的自由存储区或堆。
动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的
指针:int *pia=new int[10];
释放:delete [] pia;该语句回收了pia所指向的数组。在关键字delete和指针之间的空方括号对是必不可少的:它告诉
编译器该指针指向的是自由存储区中的数组,而并非单个对象。
7、无法使用string对象初始化字符指针:
例:
string st("Hello world");
char *str=st; //这样的赋值是错误的
char *str=st.c_str(); //这样的赋值是正确的(不完全正确)
c_str函数返回c风格字符串,其字面意思是:返回c风格字符串的表示方法,即返回指向字符数组首地址的指针,该数组
存放了与string对象相同的内容,并以结束符NULL借宿。
因为c_str返回的指针指向const char类型的数组,所以(char *str=st.c_str(); )初始化失败,这样做事为了避免修改该
数组。正确的初始化应为:const char *str=st.c_str();
8、数组指针和指针数组:优先级:()>[]>*
int *ip[4]:指针数组:存放指针的数组,即有4个元素,每个元素存放指针。p[0]、p[1]、p[2]...p[n-1],它们分别是指针变量可以用来存放变量地址
将二位数组赋给指针数组
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
int (*ip)[4]:数组指针(也称行指针),指向数组的指针,即(ip+0)指向第0行(每行有四个元素),(ip+1)指向跨越第0行(四个元素),指向第一行。
首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是4,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
用typedef简化指向多维数组的指针
int ia[3][4];
typedef int int_array[4];
int_array *ip=ia;
for(int_array *p=ia;p!=ia+3;++p)
{
for(int *q=*p;q!=*p+4;++q)
{
cout<<*q<<endl;
}
}
//动态创建二位数组
const int column=4;
const int row=6;
int **arr=new int *[row];
for(int i=0;i<row;++i)
arr[i]=new int[column]();
for(int i=0;i<row;++i)
delete [] arr[i];
delete [] arr;
9、位与操作(&)需要两个整型操作数,在每个位的位置,如果两个操作数对应的位都为1,
则操作结果中该位为1,否则为0。
位异或操作符(^)也需要两个整型操作数。在每个位的位置,如果两个操作数对应的位只有
一个(不是两个)为1,则操作结果中该位为1,否则为0。
位或操作符(|)需要两个整型操作数,如果两个操作数对应的位有一个或者两个都为1,则操作
结果为1.
10、 int i=0,j;
j=++i;
cout<<"j="<<j<<"i="<<i<<endl;
j=i++;
cout<<"j="<<j<<"i="<<i<<endl;
输出j=1,i=1;//先自增再赋值
j=1,i=2;//先赋值,再自增
vector<int>::iterator iter=ivec.begin();
*iter++:等效于*(iter++),子表达式iter++使iter加1,然后返回iter原值的副本作为该表达式
的结果。因此,解引用*的操作数是iter未加1前的副本。
11、sizeof操作符
(1)对char类型或值为char类型的表达式做sizeof操作保证得1。
(2)对引用类型做sizeof操作将返回存放此引用类型对象所需的内存空间大小。
(3)对指针做sizeof操作将返回指针所需的内存大小;注意,如果要获取该指针所指向对象的大小,则必须对该指针进行解引用。
(4)对数组做sizeof操作等效于将对其元素类型做sizeof操作的结果乘上数组元素的个数。因为sizeof返回整个数组在内存中的存储长度,所以用sizeof数组的结果除以sizeof其元素类型的结果,即可求出数组元素的个数。
12、显示转换(强制类型转换):static_cast dynamic_cast const_cast reinterpret_cast
(1)dynamic_cast支持运行时识别指针或引用所指向的对象。
(2)const_cast 将转换掉表达式的const性质。
例:const char *pc_str;
char *pc=const_cast<char *>(pc_str);
(3)static_cast 编译器隐式执行的任何类型转换都可以游static_cast显示完成
double d=97.0;
char ch=static_cast<char>(d);
(4)reinterpret_cast reinterpret_cast 通常为操作数的位模式提供较低层次的重新解释
13、使用预处理器进行调试:程序所包含的调试代码尽在开发过程中执行。当应用程序已经完成,并且准备提交时,就会将调试代码关闭。可以使用NDEBUF预处理变量实现有条件的调试代码:
int main()
{
#ifndef NDEBUG
cerr<<"starting main"<<endl;
#endif;
}
14、int *func(){} //该函数返回一个int型指针,该指针可以指向数组中的一个元素
15、如有下列函数
string::size_type find_char(string &s,char c)
{
string::size_type i=0;
while(i!=s.size() && s[i]!=c)
i++;
return i;
}
当调用时:find_char("HELLO WORLD",'o');会出现编译错误。
总结:当不修改函数的实参时,应该把形参定义为const对象
string::size_type find_char(const string &s,const char c)
16、数组形参的定义
void print(int *); void print(int []); void print(int [10]); 是等价的,都是传递数组的首地址
17、inline函数避免函数调用的开销
例如:普通函数:const string &shorterString(const string &s1,const string &s2)
{
return s1.size()<s2.size()?s1:s2;
}
缺点:电泳函数比求解等价表达式要慢得多:调用前要先保存寄存器,并在返回时恢复;复制实参;程序还必须转向一个新位置执行
内联函数:
inline const string &shorterString(const string &s1,const string &s2)
{
return s1.size()<s2.size()?s1:s2;
}
调用:cout<<shorterSting(s1,s2)<<endl;
在编译时将扩展为:cout<<(si.size()<s2.size() ? s1:s2)<<endl;
从而消除了把shorterString写成函数的额外执行开销。
注意:一般来说,内联机制适用于优化小的、只有几行的而且经常被调用的函数。大多数的编译器都不支持递归函数的内联。一个
1200喊的函数也不太可能在调用点内联展开。
18、this指针的引入
在调用成员函数时,形参this初始化为调用函数的对象的地址。
bool same_isbn(const Sales_item &rhs)
{return isbn == rhs.isbn}
编译器重写:Sales_item::same_isbn(&total,trans);
19、bool same_isbn(const Sales_item &rhs)const
{return isbn == rhs.isbn}
跟在Sales_item成员函数声明的形参表后面的const所引起的作用:const改变了隐含的this形参的类型。
编译器重写:Sales_item::same_isbn(const Sales_item *const this,const Sales_item &rhs)cosnt 。
使用const的函数称为常量成员函数。由于this是指向const对象的指针,const成员函数不能修改调用该函数的对象。