一、如何用C++的源文件产生一个可执行程序
一个C++程序由一个或者多个编译单元组成。每个编译单元都是一个独立的源代码文件,通常是一个带.cpp的文件,编译器每次编一个文件编译单元,生成一个以.obj或者.o为后缀的目标文件。程序就是运行在这些目标文件上。这些目标文件都是二进制的文件。一旦所有的编译单元都生成了目标文件,我们就用连接器将他们连接成一个可执行的.exe文件。
我们可以在一个.cpp文件中实现多个类,也可以将多个类分散的放在多个.cpp文件中。当修改了某一个.cpp文件后,我们只需要重新编译这个文件,就可生成新的可执行程序。
通过编译器,可以将两个cpp文件分别编译生成.o文件。在通过.o文件生成可执行程序。例如:
CC -c main.cpp
CC -c test.cpp
CC main.o test.o -o test
最后执行./test 123
在main文件中调用了test文件中的函数。在调用前需要声明test中的函数。如果文件过多的时候会出现重复声明的问题,比如A中声明了B中的函数,C中也声明了B中的函数,这时候用A调用C时会出现这个函数声明重复的错误,连接器会报错输出“unresolved symbol”(不可解析的符号),所以在实际应用中,我们是通过将这个函数声明写入头文件中,如.h\.hh\.phh\.hxx等,在我们需要调用这个函数的时候,包含这个函数的头文件就可以了。格式如下:
#ifndef TEST_H
#define TEST_H
int test(int);
#endif
不要认为程序就是一些目标文件组成的。实际情况中,可执行程序通常都会连接许多库,这些库已经实现了许多现成的功能。库主要有两种类型:
l 静态库可以直接放入可执行程序中,就像目标文件一样,但会使可执行文件变得很大。
l 动态库DLL位于机器上的标准位置,并且在应用程序启动的时候自动加载他们。
C++的可编译性和对性能的追求是不同于Java和C#等语言的。C++不会在运行时检测数组是否越界,没有垃圾信息收集器回收那些分配出去但是不再使用的动态内存。
高字节在后的系统架构(如PowerPC和SPARC),23位变量值0x12345678会存储为4个字节:0x12、0x34、0x56、0x78。对于高字节在前的系统架构(比如Intel x86体系)。这些字节存储顺序会被颠倒过来。这样就会把内存区域中数据复制到磁盘或者在网络上发送二进制程序中产生差异。
二、C++语法
内敛函数(inline)在类定义中已经实现了方法。与之对应的是把函数原型放在头文件中,而把实现这些函数的代码放在.cpp文件中。从语法上讲这两种方法是等效的,但是当我们调用内联函数时,绝大多数编译器只是对此函数做简单的扩展,而不会生成实际的函数调用。这样会是程序运行效率更高,但是相应的会带来应用程序的增大。所以,只有非常简单的函数才实现为内联函数。
Virtual fun()=0//纯虚函数——没有默认实现代码并且必须在子类中实现的函数。
C++中,如果是只想某个实例对象的指针要访问类方法时,必须使用“->”,如果是实例则可以用“.”。
New动态分配的对象一般分配在“堆”上,而局部变量(在函数中定义的变量)则存储在“栈”里。
将指针定义为const类型,可以限制指针不让其修改它们,只能用于调用常量成员。
Typedef重新定义数据类型,设定成其他的名字(别名)
C++中的数据类型强制转换,此语法功能非常强大。可以改变指针类型,移除const等等。
C++中引入4种具有更为准确语义的新强制转换类型。
1、Static_cast<T>()可用于把指向A的指针强制转换为指向B的指针,其约束条件是类B必须是类A的子类。
2、Dynamic_cast<T>(),与Static_cast<T>()类似,只是它使用的是运行时类型信息的方法来验证与这个指针相关的对象是否是类B的一个实例。如果不是,强制转换就会返回一个空指针null。
3、Const_cast<T>()添加或移除对指针或者引用的const限定。
4、Reinterpret_cast<T>()把人一类型的指针或者引用转换成任意的其他类型。
可能有点模糊,我的理解就是Static_cast<T>()这种情况是对那些操作转换规范不会出问题的转换用的,Dynamic_cast<T>()则是对那些可能出错的转换用的,因为有返回信息可以查看是否转换成功,希望有更好的解释在下面告诉我。其他两个还算比较清晰。
Static静态关键字的功能是让被此关键字声明的变量或者函数只能在此编译单元下用,称这种情况较静态连接(static linkage)。其他情况则成为外部连接(external linkage)。
命名空间的使用除了::前面加上这个命名空间名,还有下面三种机制:
定义命名空间的别名
Namespace a = aaaaaaaaaaaaaaaaaaaaaaaaa;
从命名空间中导入一个简单的标识符
Using aaaaaaaaaaaaaaaaaaaaaaaaaaa::fun;
fun();
只用一条指令导入整个命名空间
Using namespace aaaaaaaaaaaaaaaaaa;
预处理器就是一个程序,可以把带“#”指令符转换成不在包含哪些指令符的源文件。
1、#include指令会把<>和””包含的头文件扩展成他们的内容。
2、#define替换成宏定义的内容。
3、#undef解除宏定义。
4、#if、#elif、#else和#endif可以处理或者跳过某部分代码。
5、#ifdef、#ifndef来避免重复包含某个头文件。
6、#error可以在编译时给出用户自定义的错误信息。