http://blog.163.com/rjwhh@126/blog/static/2333770720080116393274/
1、 在C语言中,要定义一个结构变量,就要在结构类型前面加上struct,而在C++则就不用重复关键字struct。如果在C语言中要这样做,必须使用typedef进行类型的重命名。
2、 用cout输出时,如果是字符串指针,则输出字符串,如果是其它类型的指针,则输出指针内容,即地址值:
char *s=”zwl”;
cout<<s; //输出字符串
cout<<(void*)s;//输出字符串的首地址值。
7、在函数定义的开头,如果给出类型名而没有给出参数名,则说明这个参数暂时不用,并且编译器也不会报错,这个功能主要是用于占位之用,在程序开发的初始阶段有一定的用处:void draw3dpoint(int x,int y, int);调用的时候可以用draw3dpoint(1,2,7),最后一个参数7没有使用。
8、进行函数重载时尽量避免使用自动转换进行匹配,建议精确地匹配参数,或者用强制转换进行匹配,并且在必要的时候添加更多的重载函数。
9、在C和C++中都可以把任何类型的指针赋值给void型的指针变量。在C语言中,也可以把void型的指针赋值给任何类型的指针变量,但是在C++中,不允许这样做,而必须进行显式的类型转换。不过在C++中最后避免使用void型指针。最后不要对参数为指针和整数的函数进行重载,这样会造成混乱,除非在你调用函数时从不使用空指针(因为空指针和0是一样的)。如果重载无法避免,那么可以使用类型转换。
10、在C语言中,只有常量表达式才可以初始化全局变量,但是C++取消了这个规定,可以用函数或者变量进行初始化,如果是用常量初始化,在编译时就进行初始化,如果是用变量或者函数,则在运行时进行初始化,这主要是调用main()函数前,会加入一些动态初始化全局变量的代码,不过这样的全局变量初始化顺序比较不确定,应该慎重使用。
11、为了避免与编译器、连接器和标准库使用的名字产生冲突,不要以单下划线作为标识符的首字符,也不要在标识符中使用双下划线。
12、new运算符只能分配一维数值,不能进行多位数组的分配,如果需要多维,应该先分配一位的指针变量,然后再对各个指针变量进行一维空间的申请并赋一维的首地址。
例子:Mytype *a0=new Mytype; //OK
Mytype *a1=new Mytype[i];//OK
Mytype **a2=new Mytype[i][j];//ERROR
Mytype ***a3=new Mytype[i][j][k];//ERROR
Mytype **a2=new Mytype*[i];//OK
Mytype ***a3=new Mytype**[i];//OK
Mytype (*a2)[6]=new Mytype[i][6];//OK
Mytype (*a3)[7][8]=new Mytype[i][7][8];//OK
还可以有另外一种写法:typedef Mytype Mytype78[7][8]; Mytype78*a3=new Mytype78[i];
也就是说new只能申请一维的数组,但是这个数组的类型可以多样。如果用delete[]释放一个动态数组漏了[],编译器不会检测出来,可能导致系统崩溃或者内存泄漏。而用delete释放一个非动态数组的指针后果也是不确定的。
13、在C语言中有realloc进行动态数组的调整,但是这在C++中只适用于内建类型,而不使用于类,因此需要用new一个新的动态数组,然后再把旧数组的数据复制到新的数组中。
14、用new创建类数组时不能带参数,但是如果有带全部默认参数或者不带参数的构造函数就会自动调用。如果没有带全部默认参数或者不带参数的构造函数就不能用new申请对象数组,但是可以申请单个动态对象。(注意,如果有自定义构造函数,系统就不在提供默认构造函数)。
15、因为new和delete不能满足要求(如上面的情况),可以对new和delete进行重载。new和delete可以在类内也可以在内外重载,但是如果在内外重载就是全局函数,会覆盖系统提供的功能,因此通常在类内重载。new和delete只能重载为成员函数,不能重载为友元函数,而且无论是否使用关键字static进行修饰,重载的new和delete均为类的静态成员函数。重载运算符new和delete的格式分别为:
void *operator new(size_t,<arg_list>);new重载应返回一个void型的指针,且至少有一个类型为size_t的参数,若new重载函数带有多于一个的参数,则其第一个参数的类型必须为size_t。
void operator delete(void *,<size_t>);delete重载应返回void类型,且至少有一个类型为void的指针参数,delete重载函数最多可以带有两个参数,若有第二个参数,则其第二个参数的类型必须为size_t。
16、delete只是负责释放指针所指向的内存,其实指针还是指向原来指向的空间,但是这是空间的内容已经变得没有意义,因为它可以被系统重新分配给其它指针变量,这种现象称为“指针悬挂”。还有一个情况就是指针变量定义后没有进行初始化,不知道它指向什么地方,这种指针称为“野指针”。因此,定义指针时一定要初始化,要不就置成NULL,delete后最后把指针变量赋值为NULL。
17、当传递引用时,实际参数并没有被拷贝,相反,形式参数则成了实际参数的化身,它们代表同一个存储区域。一个引用可以和派生类对象的基类部分联编在一起,这和指针一样。在函数中返回引用时不能返回局部变量的引用,要注意生存期的问题。同时如果返回值是一个引用就可以作为左值变量,当作一个变量进行赋值。
18、如果在函数定义中是一个引用,则传给它错误的类型、表达式或者直接量,编译器会报错,这主要是参数传递时的临时变量引起的问题。
例如:
void func(int &ri);//假设定义这个的一个函数
//
int int_var;
long long_var;
func(int_var);//OK
func(long_var);//ERRORlong_var到int会隐式转换,生成临时变量在进行引用
func(int_var*2);//ERROR
func(5);//ERROR
19、常引用是用const修饰的引用,它常常用做形式参数,用来限制对实参对象的修改。也用来修饰一个常量或者变量,用来表示通过该引用不能改变被引用的常量和变量的值。
20、在内联函数中不允许出现循环语句、开关语句和递归调用语句;在类内部定义并实现的成员函数都是内联函数,但是在类内定义成员函数时,如果使用了for,while,do…while,switch等语句,该成员函数会自动转为非内联函数;内联函数中不能有静态数据,内联函数不能有数组说明。
21、const与C语言中的#define差别主要是宏不是符号常量,没有数据类型也没有值,在内存中没有地址,只是在预处理的时候进行替换,不做类型检查,而const定义的常量和变量一个,只是内容不可变而已,编译时进行类型检查。
1)可以用于说明符号常量,表明符号代表的是一个常量:
const double PI=3.14;
2)说明数组常量:
const char C_CHAR={‘a’,’b’,’c’,’d’};
3)说明对象常量:
const CInline1 OC; <类名> const<对象名>,<对象名>……const<类名><对象名>,<对象名>。
4)说明引用常量:const int &ri=i
22、指针和const的组合可以有三种情况:
指向常量的指针:const int *p;
常指针:char *const pc=”a”;
指向常量的常指针:const char *const pc=”a”;
23、const说明函数参数和返回值:用const修饰函数参数意味着传递过来的实参在函数中是不能被修改的,如果用const修饰用户定义类型函数的返回值,就表示该返回值不能被修改,如果返回指针或者引用,const就可以保证该返回的存储区域不被修改,即不能作为左值。这两个运用是const强大功能的最重要体现。
24、const在类中的运用,可以用于修饰常成员函数,表示这个函数不可以改变对象数据成员。只有常成员函数才可以操作常对象,也就是说通过常对象只能调用常成员函数,即常对象只能调用const修饰的成员函数。特别注意的是:仅在const方面不同的成员函数是可以重载的,编译器能够正确区分这两个函数,但是有const修饰参数和没有const修饰参数的两个函数是不能重载的,因为编译器在判断重载的时候在函数参数方面只考虑类型。对调用对象来说,一般对象优先调用重载的一般函数,其次才是const函数。若一个数据成员用const进行修饰,就称为常数据成员,常数据成员的初始化比较特别,它必须在定义对象的时候,通过在构造函数后面加上成员初始化列表完成(对于普通数据成员也可以这样初始化)。静态常数据成员则在类外面可以直接赋值。
25、类型转换就是将一种类型转换为另一种类型,标准类型之间可以互相转换,类类型之间,标准类型和类类型之间也可以互相转换。对于标准类型C++提供了隐式转换和显式转换两种机制。而自定义类类型到基本类型的转换则可由自定义的构造函数和类型转换函数来实现。利用构造函数只能完成从基本类型到类类型的转换,要完成从类类型到基本类型的转换则需要定义类型转换函数。利用构造函数进行类型转换必须有一个前提,那就是类中一定有一个可以带一个参数的构造函数,且这个函数能够完成从基本类型到类类型的转换。从类类型到基本类型的转换其实就是类类型转换重载函数。
格式:operator <基类型名>()
{……return<基本类型值> }
定义类类型转换函数时,应该注意:转换函数是类的非静态成员函数,定义类类型转换函数时,不进行返回值的类型说明(因为<类型名>就代表了它的返回值类型),也没有形参。
26、同一个类的两个不同对象是可以访问对方的专有成员的,例如:
void IntArray::assign(Int Array *source)
{
SetSize(source->getSize());
Memcpy(elems,source->elems,getSize()*sizeof(int));//假设elemes是一个私有数据成员
}
也就是说C++的存取控制是被限制在类这个级别上的,但是有些语言将存取控制限制在对象级别上,也就是说没有对象可以访问其它对象的私有成员。
27、存取级别不是成员函数的特征,因此存取控制并不会影响编译器选取重载函数,即编译器处理对成员函数的调用时并不考虑存取级别,它只是在处理完函数调用后才会检查存取级别,如果存在存取冲突,就给出错误信息(注意这和const说明的常成员函数不一样)。利用这个原理可以禁止拷贝和赋值运算符的调用:在private:中声明拷贝构造函数和赋值运算符函数,因为这些由类提供,编译器就不在提供,因此如果使用了这两个函数的功能就会出现链接错误,从而达到禁用的目的。
28、可以通过friend声明授予其它类或者非成员函数友元身份,使得具有友元身份的类和函数可以访问本类的数据成员。注意friend的声明是单向且不可传递的。一个类的友元类的派生类也不是这个类的友元类,即友元关系和派生是完全独立的。
29、把派生类指针转换成基类指针称为向上转换(up-casting),而把基类指针转换为派生类指针称为向下转换(down-casting),名称的前缀说明了在派生树中行进的方向。
30、成员函数的重载
在一个类中有多个同名函数时,就是成员函数的重载,编译器会为每一个调用选择最为匹配的函数,但是,如果在派生类中再添加一个同名函数的话,这个派生类中的新函数将会隐藏掉基类中所有的同名函数,就像局部变量会隐藏掉同名的全局变量一样,也即派生类的成员函数和基类的成员函数之间是不会发生重载的,而会发生覆盖。如果要调用基类的成员函数,只能加上基类名::,在这边还应该注意虚函数的作用以及多态性。
31、一个构造函数是没有返回类型的,即使连void都没有。和其它成员函数一样,构造函数和析构函数可以声明为私有类型,这样限制了类被实例化的方式,对于那些不能仅由一个类创建或销毁的特殊类比较有用。隐式的构造函数和析构函数都是公有型的。
32、如果使用跳转(主要是goto和switch语句),跳过了对象的声明,那就必须跳出包括对象声明的那个语句块。
33、注意在声明变量的时候进行初始化和后面的赋值中的两个“=”是不一样的:在C语言中,赋值和初始化之间只有细微的差别,它们都是把操作数的位拷贝到目的操作数。在C++中,赋值是改变已经被构造好的对象的值,而初始化则是要构造一个新对象,并且同时赋给它一个值。在C++的前身(含类的C),这种初始化的实现实际上是调用缺省的构造函数,然后再调用赋值运算符函数。然而在C++中,使用一个新的构造函数:拷贝构造函数进行优化。
main()
{
TextBox t1;
TextBox t2=t1;//会调用拷贝构造函数
t2=t1;//会调用赋值运算符函数
}
如果没有认为提供拷贝构造函数,编译器会提供一个隐式的拷贝构造函数,它只是简单地通过在源操作数和目标操作数之间进行位拷贝实现的,没有指针变量的类通常可以使用隐式的拷贝构造函数。特别重要的一个是如果重写了隐式的拷贝构造函数,也就必须重写隐式的缺省构造函数,因为编译器在这种情况下就不会再提供一个隐式的缺省构造函数。
34、拷贝构造函数不仅仅是在初始化一个对象时调用,使用对象作为行参,并调用该函数的时候也会调用对应的拷贝构造函数,就是按值传递的时候会调用。当函数结果使用量值返回时,拷贝构造函数也会被调用。
35、输出对象:
输出对象可以使用两种方式,或者这两种方式的组合:
class Rect
{
public:
void print(ostream*os);
}
然后在对<<运算符进行重载:
ostream &operator<<(ostream &os,Rect &r)
{
r.print(&os);
return os;
}
注意operator<<()是一个全局函数,每一个类都有它自己的全局operator<<函数,调用时主要是靠它们的第二个参数进行区分。