一、头文件和源文件的区别
1.头文件:变量、函数的声明,类的定义、宏的定义......
源文件:变量的定义,函数的定义实现
2.单独的头文件不参与编译,源文件参与编译
int a;//变量的定义
int b=10;//变量的定义
extern int c;//变量的声明
若在头文件中定义变量,当多个源文件包含同一个头文件时,会出现变量重定义问题
void fun();//函数的声明
//--------------------
void fun() //函数的定义
{
cout<<"fun()"<<endl;
}
类在头文件中声明,在源文件中初始化化时需要添加作用域
//AA.h
class CTest
{
public:
int m_a;
const int m_b;//常量在初始化参数列表进行初始化
static int m_c;//类内定义类外初始化化,但不能在头文件中初始化,会出现重定义,应该在源文件中初始化
CTest();
~CTest();
void fun();
static void fun2();
void fun3() const;
virtual void fun4();
}
//------------------
//AA.cpp
int CTest::m_c=30;//静态函数在源文件中初始化
CTest::CTest():m_a(10),m_b(20)
{
cout<<"m_a="<<m_a<<endl;
}
CTest::~CTest(){
cout<<"~CTest()"<<endl;
}
void CTest::fun(){
cout<<"fun()"<<endl;
}
//static要去掉
void CTest::fun2(){
cout<<"fun2"<<endl;
}
//const 要保留
void CTest::fun3() const{
cout<<"fun3"<<endl;
}
//virtual要去掉
void CTest::fun4(){
cout<<"fun3"<<endl;
}
二、头文件的重复包含问题
#pragma once//和编译器沟通,告诉编译器,当前头文件在源文件中只包含一次
另一种方法是基于宏的逻辑判断
#ifndef __宏__
#define __宏__
...头文件中的代码...
#endif
//宏的名字与头文件名进行绑定,例如AA.h->__AA_H__
对比:
1.#pragma once 效率高,代码编译效率快
2.#pragma once不用担心宏重名问题
二、程序生成过程
1.预处理:源代码文件main.cpp -> main.i预处理后的文件
(1)#include 头文件的展开
(2)删除注释
(3)#define宏的替换
(4)#ifdef、#ifndef、#endif、#if、#else、#elif等预处理指令处理
2.编译阶段:将预处理后的文件进一步处理形成汇编文件,包含汇编代码,预处理后的文件main.i -> main.asm(汇编文件)
对代码进行,语义分析、语法分析、词法分析和优化
3.汇编阶段:将汇编文件按照汇编代码一步步生成目标机器指令,从main.asm -> main.obj(目标机器文件(二进制文件))
4.链接阶段:通过链接器将生成的目标机器文件和库文件进行链接整合到一起,形成可执行文件(.exe(二进制文件))
1.编译期:将源代码交给编译器,进行编译生成可执行文件的过程
2.运行期:将可执行文件交给操作系统运行,直到程序运行结束的过程
访问修饰符和类的作用域属于编译期的概念
类的对象(实例化),以及指针引用等属于运行期的概念
虚函数不能是私有的:因为多态是运行期多态,访问修饰符是编译期存在的
三、宏
//AA.h
//----------------
#define AA 10
// \连接当前行和下一行的代码,通常最后一行不加\,\后面不能加任何东西包括空格、tab、注释
#define BB for(int i=0;i<10;i++){\
cout<<i<<endl;\
}
//宏可以加参数
#define CC(COUNT) for(int i=0;i<COUNT;i++){\
cout<<i<<endl;\
}
//宏只是替换,不会做表达式计算求解
#define DD(A,B) A*B
//--------------------------
//main.cpp
cout<<AA<<endl;
BB
CC(5)
cout<<"---------------"<<endl;
int a=DD(2+3,4);
cout<<a<<endl;//输出结果是14
“#”的一些特殊用法
#define AA(A) #A //“#”代表在两边加上""变成字符串
#define BB(B) #@B //相当于在变量两边加上单引号‘’,变成字符
#define CC() int a##b=10 //##是连接作用,a##b相当于ab
可以使用#undef取消宏的使用,限制宏的使用范围
#undef AA
宏的优点:可以代替程序中常用的常量或者表达式,之后如果需要维护,只需要维护一份就可以
宏的缺点:
1.不方便调试
2.难看出一些问题,可能出现安全问题
3.带参数的宏不能进行表达式的计算
四、内联函数
inline是内联函数的关键字,编译阶段进行替换,可以提高函数的运行效率
对于一些简单的函数来讲,可能调用函数的时间比执行时间都长,可以使用内联函数
缺点:如果程序中多次调用内联函数会占用大量空间,相当于空间换时间的做法
inline int add(a,b)
{
return a+b;
}
适用于函数代码量少,逻辑简单的函数,对于一些含有for switch while dowhile 的不推荐使用
inline属于建议性关键字,是否使用取决于编译器的判断,递归函数和虚函数一定不是内联函数
不太重要的一点:在类内定义并初始化的默认为内联函数,类内定义类外初始化的默认不是内联函数