天天看点

第六课 C++探索“程序”一、头文件和源文件的区别 二、头文件的重复包含问题二、程序生成过程三、宏四、内联函数

一、头文件和源文件的区别

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属于建议性关键字,是否使用取决于编译器的判断,递归函数和虚函数一定不是内联函数

不太重要的一点:在类内定义并初始化的默认为内联函数,类内定义类外初始化的默认不是内联函数

继续阅读