3.5.1 实时定义变量
定义变量时,c和c++有显著地区别。这两种语言都要求变量使用前必须定义,但c强制在作用域的开始出就定义所有的变量,一遍在编译器创建一个快事,能给所有的这些变量分配空间。
3.6全局变量
如果在一个文件中使用extern关键字声明另一个文件中存在的全局变量,那么这个文件可以使用这个数据。
{o}意思是“不要从这个文件生成可执行的文件,编译它是为了把它连接进一些其他的可以执行的文件中”
Static 和 全局变量的区别是
即使使用了extern仍然无法得到该变量,static变量只能在指定的作用域内使用。
枚举变量enum
枚举变量可以转化为int,但是不可以从int类型转化为枚举类型,但c是允许的,c++不允许。
C++中void*如果不经过转换成如int*是不允许赋值,但在c中确是可以的。
如果从const转换为非const或从volatile转换成非volatile,可以使用const_cast.
Reinterpret_cast是转换成的完全不同的,如可以不struct转换成(int*)进行处理,显得特别不安全。
指向函数的指针数组,为了选择一个函数,只需要使用数组的下标,然后间接引用这个指针。这种设计方式对于要从表中添加或删除函数十分有用。
Make解决了如果改变一个文件就要编译全部文件的问题。当输入make时,make程序在当前目录中寻找名为makefile的文件,该文件作为工程文件已经被建立了。‘#’为注释的符号。许多make程序中要求规则以tab开头。
通过#define定义一个或更多的调试标记,可以测试一个使用#ifdef语句和包含条件调试代码的标记。当认为调试完成了,只需要用#undef标记。
#define debug
#ifdef debug
#endif
后缀规则告诉make可以根据文件的扩展名考虑怎样构建程序而不需要显示规则构建一切。
Cpp=mycomplier
.suffixs:.exe .cpp
.cpp .exe:
$(cpp) $<(无论怎样都要触发的规则)
make debug时会从新构建所有带有调试信息的文件。
Delete[]myArray;
C库的问题之一是必须向用户认真的说明初始化和清除的重要性。
头文件是存放接口规范的地方。将所有的函数声明都放在一个头文件中,并且将这个头文件包含在使用这些函数和定义这些函数的任何文件中,就能确保整个系统中声明的一致性。
C和c++都允许重声明函数,只要两个声明匹配即可,但是两者都不允许重声明结构。
C++的编译器要求在引用任一变量之前声明。
嵌套结构并不能自动获得访问private成员的权限。要活的访问私有成员的权限,必须:首先声明一个嵌套结构,然后声明它是全局范围使用的一个friend,追后定义这个结构。结构的定义必须与friend的声明分开,否则编译器将不它看做成员。
传递到构造函数的第一个参数this指针,也就是调用这一函数的对象的地址,对于构造函数来说,this指针指向一个没有初始化的内存块,构造函数的作用就是正确的初始化内存块。
析构函数,当对象超出它的作用域是,编译器将自动调用析构函数。
例如for循环。
一个变量可以再某个程序范围内的任何地方定义,所以在这个变量定义之前是无法对它分配内存空间。通常,编译器跟可能像c编译器一样,在一个程序块的开头就分配所有的内存。保证对象产生时同时被初始化。
聚合(aggregate),定义一个数组而没有给出一列初始值是,编译器并不会去做初始化的工作。Int b[6]={0};
结构也是一种聚合类型,可以用同样的方式初始化
无论是对所有成员都是公共的struct还是一个带有私有成员的class,所有的初始化工作都必须通过构造函数来完成,即使正对一个聚合初始化。尽量自己指定构造函数而不让编译器来完成。
Overload
全局的函数和类内部的函数不会放生冲突,类内部的函数用类名::函数名来表示。Void print(char); void print(float);可能会产生类似于_print_char和_print_int
可以用范围和参数来重载,但不可用返回值来重载,因为c总可以用一个函数但会忽略它的返回值。名字修饰会给我们提供一个安全网,这也就是人们常说的类型安全连接。
唯一的区别在于:该联合的两个变量占用统一内存空间。如果匿名union在文件作用于(在所有函数和类之外),则它必须被声明为static,使他由内部链接。
函数重载的默认参数Stash(int size,int initQuantity=0);
使用默认参数规则:第一,只有参数列表的后部参数才是默认的,也就是,不可以在一个默认参数后面个一个非默认参数。第二,一旦再也个函数调用中开始使用默认参数,那么在这个参数后面的所有参数都必须是默认的。在C++中,在函数定义时,并不定需要标示符。这种语法允许把一个参数用做占位符而不去用它。其目的在于以后可以修改函数定义而不需要修改所有函数的调用。
C++中的const默认是内部链接,也就是const仅在const定义过的文件里才可见,而在连接时不能被其他边一单元看到。当定义一个const时必须附一个值,除非用extern修饰。由于extern意味着使用外部链接,因此必须非配存储空间,几个编译单元能够引用。
如果在运行期间产生的值初始化一个变量而且该变量在生命周期是不变的,则用const限定该变量是程序设计。
Const修饰聚合时,const意味着“不能改变的一块存储空间”。然而,不能在编译期间使用它的值,因为编译器期间不需要知道存储的内容。
可以改变它的值。在c中const默认的是外部链接需要创见内存空间
,在c++中默认的是内部链接,并且不必创建内存空间。
Const修饰的指针,是在标示符的开始处读它并从里向外读,const修饰“追靠近”它的那个。
Const int *u;u不是const的,所以没必要赋初值;
Int* cosnt v;v指针是const的,所以必须赋初值。
可以把一个非const的指针赋值给一个const的指针,但是不可以把一个const指针赋值给非const指针。
Const限定函数参数及返回值;如果按值传递对象,对客户讲,用const限定没有意义,如果传递并返回地址,const将保证该地址的内容不会改变。在函数内部用const优于在参数表中用const限定参数。
Error:l-value—compile-time error
再求表达式的值的期间,编译器必须创建临时变量(temporary object)。像其他任何对象一样,他们需要存储空间,并且能够构造和销毁。当计算法表达的值自发动的形成的临时变量自动的成为const。
当传递一个参数时,首先选择按引用传递,而且是const引用。
为了保证一个类对象为常量,引进const成员函数:const成员函数只能对于const对象调用。
类:
在一个类里使用const意味着“”在这个对象生命期内,它是一个常量。然而,对这个常量来讲,每个不同的对象可以含有一个不同的值。构造函数初始化列表是初始化所有const量的。
如果声明一个成员函数为const,则等于告诉编译器该成员函数可以为一个const对象所调用。一个没有明确声明为const的成员函数被看成是将要修改对象中的数据成员的函数,编译器不允许一个const对象调用。
声明const成员函数的语法,首先注意前面的带const的函数声明,他表示函数的返回值是const,但这不会产生想要的结果。相反,必须把修饰符const放在函数参数列表的后面。
Int f()const;对f任何方式改变或者调用一个非const成员函数,编译器都把它标记成一个错误。
指定mutable,指定一个特定的数据成员可以在一个const的成员函数中改变。
Volatile只能调用volatile函数。
使用预处理器宏,宏像一个函数的调用,但并不是总这样,隐藏了错误。2.c++特有的:预处理器不允许访问类的成员的数据。
表达式在宏内展开,所以他们的优先级不同于所期望的优先级。解决,可以用括号通过在宏内的各个地方括弧起来。
在c++中应该只是使用内联函数而不使用宏。任何在类中定义的函数都是内联函数,但也可以在非类的函数前面加上inline关键字。
使用内联函数的目的是减少函数调用的开销。但是,假如函数较大,由于需要在调用函数的每一处重复复制代码,这样使代码膨胀。
任何类的循环都被认为太复杂而不扩展为内联函数。循环在函数里可能比调用要花费更多的时间。
假如要显示的或隐式的取函数的地址,编译器也不能执行内联。编译器必须为函数产生地址非配内存从而产生一个函数地址。
C++语言规定:只有类声明结束后,其中的内联函数才会被计算。
标志粘贴直接用“##”实现,在写代码时是非常有用的。它允许两个标示符并把它们粘贴在一起自动产生一个新的标示符。
#define FIELD(a) char* a##_string,int a##_size
Class Record
{
FIELD(one);
FIELD(two);
};
C和c++中都允许在函数内部定义一个static对象,这个对象将存储在程序大的静态存储数据区中,这个对象只在函数第一次调用时初始化一次,以后保持它的值。
无论什么时候设计一个包含静态变量的函数时,都应该记住多线程问题。静态对象的析构函数在程序main()中退出时,或者标准的c库函数exit被调用时才调用。Atexit()注册的函数可以在所有对象的析构函数之前调用。静态对象的销毁按与初始化时相反的顺序进行的。全局对象的obj类对象,所以构造函数总是在main函数之前就被调用。
在文件作用域内所有的名字对程序的所有编译单元都是可见的。外部链接。Static是内部链接。没有进入局部变量时static可以改变可见性。
确保每个翻译单元只有一个未命名的名字空间。 Local类中不能有静态数据成员。
引用和拷贝构造函数
拷贝构造函数,医用从实现现有的对象中产生新的对象。编译器使用拷贝构造函数通过按值传递的方式。
1) 当引用被创建是,它必须初始化
2) 一旦一个引用被初始化为指向一个对象,它就不能改变为另个对象的引用。
3) 不可能NULL引用
无论引用关联的是什么都应该在外部存在内存空间。
函数会接受临时对象,这个临时对象是由另一个函数的返回值创立的或有函数使用显示创立的。
构造函数是控制通过船只方式传递和返回用户定义类型。.
当使用位拷贝时,构造函数函数没有调用。这个函数可以拷贝构造函数。
所有的指针都需要地址,但在类内不是没有地址的;选择一个类的成员意味着在类中偏移。只有把这个偏移和具体对象的开始地址结合,才能得到实际地址。
运算符重载
“调用”运算符是要把运算符放置在参数之间。第二个不同是由编译器决定调用哪个“函数”。
运算符重载只是一个工具。仅仅只是一种语法上的方便,。
只有那些包含用户自定义类型的表达式才能有重载的运算符。(对于一元没有参数,对于二元是一个参数-此时该类的对象用做左侧参数。)
Operator=只允许作为成员函数
在适当的地方用类型转换可以减少许多运算符重载。
引用计数。如果需要大量的内存或过高。可以是一块存储单元具有智能,他知道有多少对象指向了它。
写拷贝技术:
当向这块内存写之前,应该没人使用它。
动态对象的创建
Malloc()只是分配了一块内存而不是生成一个对象,所以它返回了一个void*类型的指针。
用delete只用于由new创建的对象。
使用delete void*可能会出错。
Void* b=new Object();
Delete b;
会出错,只会释放Object对象的内存,不会调用析构函数。
继承和组合
基类的私有成员仍然占有存储空间,只是不可以直接访问他们。
希望基类的共有成员任然是共有的用public 基类
没有对所有的成员对象和基类对象的构造函数进行调用自谦,就没有办法进入该构造函数体。
只有继承,才能重新定义它的函数,而对于成员函数对象,只能操作这个对象的公共接口。
构造是从类层次的追根处开始,而在每一层,首先调用积累的构造函数,然后调用曾源对象构造函数。成员对象在类中声明的次序所决定的。
名字隐藏:
对普通函数的重定义,而基类的成员函数是虚函数的情况,又可以重写。
在任何时候重新定义了基类中的一个重载函数,在新类之中所有其他的版本则自动隐藏了。
Private的继承如果想用共有的方法,可以用公共的生命。
无论何时创建了自己的拷贝构造函数是,都要正确的调用基类的拷贝构造函数。