转自:http://blog.chinaunix.net/uid-20196318-id-2420689.html
以前学C++时记的比较,因最近开发用C++较多,把笔记翻出来复习了一下,跟大家分享一下。
1. class、struct、union保留字都可以用来声明和定义类。class中成员默认为private类型,struct、union与C语言兼容,成员默认为public类型。
2. 只有当类没有显式的定义构造/析构函数时,C++才会提供默认的构造/析构函数;默认的构造函数只负责创建对象,不做任何初始化工作。
3. 程序正常退出时,析构函数会被隐式调用;非正常退出(如abort)则析构函数不会被调用,可能导致系统资源没有及时得到回收。构造函数只能被隐式调用,析构函数可以被显式调用。
4. 对象在退出其作用域时自动析构,其对象的析构顺序与其创建的顺序相反;常量对象在创建之后立即析构。
1. private说明的对象成员是完全私有的,即便是派生类的后代对象也不能访问这类成员。
2. protected说明的对象成员是私有的,受保护的,该对象派生的后代可以访问这类成员。
3. public说明的对象成员是完全公开的,任何对象都能访问这类成员。
4. 无论什么类型的的对象成员,都可以被友元函数访问。
5. 类的访问权限只能防止无意识的越权访问,通过强制类型转换,可对类的成员无限制的进行访问。
1. 不管是否出现inline保留字,在类体内定义的任何函数成员都会自动成为内联函数。
2. 在类题外定义内联函数,必须用inline显式的加以说明。
3. 内联函数的定义必须出现在内联函数第一次调用之前,否则以普通形式调用(内联失败)。
1. 对于简单类型以及没有定义构造和析构函数的类,malloc/free及new/delete两种内存分配方式可以混用;若类定义了构造函数和析构函数,则最好使用new和delete来分配和释放内存。
2. 在用new为数组分配空间时,数组的第一维下标可以是动态的,其它维则要求是静态的,即必须为整型常量或常量表达式;如果数组元素的类型为类,且希望用new创建对象数组,则相应的类必须定义无参数的构造函数,如果没有定义任何构造函数则使用C++的无参数构造函数。
3. 在全局空间重载new、delete运算符,则全局空间中的使用new、delete将是重载版本。
4. 在类空间重载new、delete运算符,则对于该类的new、delete将使用类的重载版本。
5. delete不需要测试指针是否为0,因为在delete的实现中已经考虑到了,没有必要进行重复的测试。
1. 类的普通成员函数比静态成员函数多了一个隐含参数this指针,隐含this指针是普通成员函数的第一个参数,该参数的类型为指向此类对象的const指针。
2. this可以用来区别与该函数成员参数同名的数据成员。
3. this可以用来访问调用该函数成员的对象、对象的地址和对象的引用。
4. 在非const成员函数中,const的声明如:C * const this;
在const成员函数中,const的声明如:const C * const this;
1. 若类定义了构造函数,则其对象必须用类定义的构造函数进行初始化;当类含有只读和引用类型的非静态数据成员时,类必须为这些成员定义构造函数。
2. 假定数据成员类A包含B类的非静态数据成员,如果B类定义了带参数的构造函数,则A类必须定义自己的构造函数,不能使用C++缺省的构造函数。
3. 构造函数必须初始化对象的对象成员,只读成员和引用成用,且只能在构造函数的函数体前初始化一次;其他数据成员可以在构造函数的函数体前初始化,也可以在构造函数的函数体内再次初始化。
4. 数据成员按其在类中定义的顺序初始化,而与他们出现在构造函数体前的顺序无关。如果简单的类型数据成员没有出现在构造函数的函数体前,则他们的值将被缺省的初始化为0。
5. 在定义的时候使用A a = A()时,与A a()一样,只调用构造函数,不调用operator=。
1. namespace保留字用于定义名字空间。名字空间必须在程序的全局作用域内定义,不能在函数内定义,最外层名字空间的名称必须在程序的全局作用域内唯一,名字空间可分多次定义。没有名称的名字空间称为匿名名字空间,每个程序只能有一个匿名名字空间。
2. using保留字用于声明程序要引用的名字空间成员,或者用于指示程序要引用的名字空间。如using A::x,则将x引入到了代码所在的作用域,不能再重复定义x;而如果使用using namespace A,则仍可定义x,但须按照A::X才能访问到A中的x。
3. 名字空间可以定义别名,以代替过长和难懂的名字空间名称,如namespace ABCD=A::B::C::D,则以后直接可以用ABCD来访问多重名字空间。
1. 使用const声明不可变的数据成员,函数成员的参数和返回值等,包含const成员的类必须定义构造函数。
2. volatile修饰的变量表示其可能被并发访问(修改),该保留字告诉编译器不要对变量的访问做任何访问优化,即不利用寄存器存放中间计算结果,直接访问对象以便获得对象的最新值。
3. 普通函数成员的参数表后可以出现const或volatile,其修饰的是函数成员隐含参数this指针指向的对象。
4. mutable修饰的数据成员是易变的,mutable成员总可以被更新,即使在const类型的成员函数中。
1. 引用时变量的别名,可以通过别名直接访问被引用的变量;而指针变量的值是变量的地址,指针是通过地址间接访问变量的值。
2. 引用必须被初始化,且初始化之后不能再做其他变量的引用(别名),引用参数则在函数调用时进行初始化。
3. 引用可用于做函数的参数或返回值,避免了传递参数时大量数据的拷贝。
4. 在目标代码中,引用是不存在的,需要使用引用的地方,已经尤其引用的对象替代了。
1. 引用变量是对引用变量的别名,对被引用的变量必须进行构造和析构,而引用变量本身没有必要构造和析构。
2. 普通引用变量必须用左值初始化,如果用右值表达式进行初始化,就会生成一个临时的匿名变量,引用参数也一样;如A &q = new A(3),则在使用完q后必须delete &q,以释放临时匿名变量的空间。
3. 非引用类型的形参是作用域限于函数的局部变量,形参对象的析构是在函数调用返回前完成的,至于形参对象的构造则是在调用时由值参数传递的,值参数传递将实参各数据成员的值相应地赋给形参的数据成员,对于指针类型的数据成员则只复制指针的值,而没有复制指针所指向的存储单元,即进行浅拷贝。如果类中含有指针成员,则进行浅拷贝可能导致内存错误,必须定义拷贝构造函数,在函数调用时使用拷贝构造函数进行深拷贝。
1. 静态数据成员用于描述类的总体信息,必须在类的体外进行定义并初始化。
2. 静态数据成员脱离具体对象独立存在,其存储单元不是任何对象存储空间的一部分,但逻辑上所哟对象都共享这一存储单元,在计算对象或类的存储空间时不能包含静态数据成员。
3. 静态数据成员描述类的总体信息,由于全局类作用与所有程序文件,故全局类的静态数据成员也必须作用于所有程序文件,即定义的时候是int P::q = 0,而不是static intP::q = 0,但在类中声明时要加上static。
4. union的数据成员必须共享存储空间,而静态数据成员各自独立分配存储单元,故静态数据成员不能成为union的成员。
5. 静态数据/函数成员可通过三种方式访问:
(1) A::member (2) a.A::member (3) a.member
1. 普通成员函数的第一个参数为隐含this指针,而静态函数成员没有隐含的this参数。
2. 构造函数、析构函数、虚函数等有this指针,若函数成员的参数表后出现const、volatile,则该函数成员的参数表必包含隐含的this指针,这些函数不能定义为静态函数成员。
3. union不能定义静态数据成员,但可以定义静态函数成员。
1. 尽量用<iostream>而不用<stdio.h>
3. 一个对象以值传递的方式传入函数体
2. …标示,能匹配任意异常)或是类型确定的参数。
7. 异常处理按catch块的顺序匹配,基类异常对象能匹配派生类对象,省略参数的catch块能匹配任意异常;故通常将派生类的catch块放到基类之前,省略参数的catch块放到最后。
8. 异常接口声明的异常有该函数引发,而其自身又不想捕获和处理的异常,异常接口定义的异常出现在函数参数表的后面,用throw列出要引发的异常类型。如:
void anyexception(); // 可引发任何异常
void noexception() throw(); // 不引发任何异常
void noexpected() throw(A,B); // 引发异常A、B
声明异常接口时,函数声明与定义的异常声明接口要一致,在函数声明时throw(A),则在函数定义时也许指定throw(A)。
1. 标准化过程中,C++生成新头文件的方法仅仅是将现有C++头文件名中的 .h 去掉,方法本身不重要,正如最后产生的结果不一致也并不重要一样。所以<iostream.h>变成了<iostream>,<complex.h>变成了<complex>,等等。对于C 头文件,采用同样的方法,但在每个名字前还要添加一个c。所以C 的<string.h>变成了<cstring>,<stdio.h>变成了<cstdio>,等等。最后一点是,旧的C++头文件是官方所反对使用的(即,明确列出不再支持),但旧的C 头文件则没有(以保持对C 的兼容性)。实际上,编译器制造商不会停止对客户现有软件提供支持,所以可以预计,旧的C++头文件在未来几年内还是会被支持。
2. 旧的C++头文件名如<iostream.h>将会继续被支持,尽管它们不在官方标准中。这些头文件的内容不在名字空间std 中。
3. 新的C++头文件如<iostream>包含的基本功能和对应的旧头文件相同,但头文件的内容在名字空间std 中。(在标准化的过程中,库中有些部分的细节被修改了,所以旧头文件和新头文件中的实体不一定完全对应。)
4. 标准C 头文件如<stdio.h>继续被支持。头文件的内容不在std 中。
5. 具有C 库功能的新C++头文件具有如<cstdio>这样的名字。它们提供的内容和相应的旧C 头文件相同,只是内容在std 中。
| 私有继承private | 保护继承protected | 公有继承public |
private | 不可访问 | 不可访问 | 不可访问 |
protected | private | protected | protected |
public | private | protected | public |
1. 公有继承被称为“类型继承”,派生类是基类的子类型。
2. 私有继承被称为“实现继承”,派生类不直接支持基类的公有接口,相反,当其提供自己的接口时,它希望重用基类的实现(组合也能达到这个目的,视情况而定)。
3. 保护继承主要用于多层次的继承。
4. 常见的多继承模式为:继承一个类的公有接口和另一个类的实现。
派生类的构造顺序1. 调用虚基类的构造函数,无论虚基类出现在继承层次上的哪个位置,它们都先于非虚基类被构造。
2. 调用基类的构造函数,按继承列表中出现的顺序。
3. 按照数据成员的声明顺序,依次调用数据成员的构造函数或初始化数据成员。
4. 执行派生类构造函数的函数体。
5. 析构顺序与构造过程相反。
1. 通过一个隐式转换,从“派生类的指针或引用”转换到“其共有基类类型的指针或引用”。
2. 通过虚函数机制。
3. 通过dynamic_cast,typeid实现从基类指针向派生类指针的转换。