头文件中应该放什么东西

发布时间 2023-05-04 19:42:36作者: 大黑耗

头文件中的内容在编译时会填充到include这个头文件的cpp文件中,所以头文件中有什么东西,相当于cpp文件中也有什么东西,如果有多个include这个头文件的cpp文件,

那么它们相当于都获得了这个头文件中的内容的一个副本,发生重定义错误。所以很多东西的定义不能放在头文件,只能放声明,否则会出现多个副本。

 

static的使用

c++引入命名空间之前,程序需要将名字声明成static的以使其只对整个文件有效,而对外部文件不可见。这个做法继承自C语言,但引入命名空间后,这个做法已经被取消了,现在的做法是在未命名的命名空间内存放对象。(c++ primer P701

所以:

1. 如果希望对象只能在文件内访问,就把它放到文件内的未命名空间。

 

2. 如果是需要多个文件共同访问的全局对象,则在其中一个cpp文件中(注意全局对象的定义不能放在头文件)使用单例模式:

extern int targetObj(){

static int obj = 1;

return obj;

}

使用单例模式的原因:

c++对“定义于不同的编译单元内的non-local static对象”的初始化相对次序并无明确定义。比如A,B两个对象分别定义于不同的cpp文件,A对象的初始化依赖B对象,所以B对象的初始化必须先于A对象。

c++保证,函数内的local static对象会在“该函数被调用期间”“首次遇到这个local static对象”时被初始化,所以把B对象放到类似上面的targetObj函数内,A在使用B时调用单例模式函数,就能保证B已经被初始化。(effective c++ P31

 

3. 对于会被多个不同的cpp文件调用的全局对象(例如上面这个单例模式函数),它的定义可以放在某个cpp文件中,前面加上extern,然后在头文件的函数声明中也加上extern

这样其他cpp文件要调用这个函数只需要include这个头文件即可。(c++ primer P54

 

总结:

头文件内只放各种对象的声明,而定义放在cpp文件中(内联函数,函数模板,模板类,模板显式实例化除外),对于多个cpp共用的全局变量,将其定义放在其中一个cpp文件(需要保证初始化顺序时使用单例模式),

然后在定义和声明前面都加上extern,其他cpp文件使用的使用直接在文件内进行extern声明即可。对于只在文件内使用的对象,将其定义放到未命名空间内。

对于宏定义和typedef,如果只在一个cpp文件中使用,则放在这个cpp文件中;若果多个源文件中需要使用这个宏,则放在头文件中定义。

// radio.h
#ifndef __RADIO_H__
#define __RADIO_H__

// 应包含内容
class Radio                                // 正确:类定义
{
    static int s_count;                    // 正确:静态数据成员声明
    static const double S_PI;              // 正确:静态常量数据成员声明
    int d_size;                            // 正确:数据成员定义
    // ...
public:
    int size() const;                     // 正确:成员函数声明
    // ...
};

inline int Radio::size() const            // 正确:内联函数定义
{
    return d_size;
}

// 不应包含内容
int Radio::s_count;                       // 错误:静态数据成员定义,应放在 .cpp 文件中
double Radio::S_PI = 3.1415926;           // 错误:静态常量数据成员定义,应放在 .cpp 文件中
int Radio::size() const { /*...*/ }       // 错误:成员函数定义,应放在 .cpp 文件中
int z;                                    // 错误:外部数据定义
extern int LENGTH = 10;                   // 错误:外部数据定义
const int WIDTH = 5;                      // 避免:常量数据定义
static int y;                             // 避免:静态数据定义
static void func() { /*...*/ }            // 避免:静态函数定义

#endif // __RADIO_H__

 在 C++ 头文件的作用域内放置带有内部链接的定义,如静态函数或数据,是合法的,但是这种做法并不理想。这样不仅污染了全局名称空间,而且包含该头文件的每一个编译单元中消耗数据空间。