C++编译与链接(0)-.h与.cpp中的定义与声明
C++中有的东西需要放在可以在.h文件中定义,有的东西则必须放在.cpp文件中定义,有的东西在不同的cpp文件中的名字可以一样,而有的则不能一样
那么究竟哪些东西可在头文件中定义,声明,哪些东西又必须在.cpp中定义,声明呢?
*以下所有的讨论都是在全局命名空间中(即不定义自己的namespace)下进行的
函数
1、在.h中只能声明函数,在.cpp中可以声明与定义函数
如果在.h中声明并定义一个函数,则该函数只能被#include一次,否则则会出现重定义错误
比如
1.h
#pragma once
void show()
{
}
a.cpp
#include "1.h"
b.cpp
#include "1.h"
error LNK2005: "void __cdecl show(void)" (?show@@YAXXZ) 已经在 a.obj 中定义
所以要避免在头文件中定义函数
2、在不同.cpp中定义的函数原型(函数返回值,函数名称,函数参数)不能完全一样,
比如如果有在两个.cpp文件中均存在
void show(){};
会出现重定义错误
内联函数
为了确保所有调用该inline函数的文件中的定义一样,所以需要是在.h文件中定义
注意这里的inline对于编译器来说只是建议性的,关于该内联函数被拒绝会在下一篇文章中介绍
typedef
在不同的cpp中可以一样
变量
1、在.h中只能声明,在.cpp中可以声明与定义一个变量
如果在.h中的定义一个变量,则该变量被include两次以上时则会出现重定义错误
2、在不同.cpp中定义的变量的名字与类型不同一样
常量
1、如果const常量是用常量表达式进行初始化的,则可以在.h中声明与定义
2、如果const变量是用非常量表达式进行初始化的,那么该变量应该在cpp文件中定义,而在.h文件中进行声明。
3、不同cpp中以定义名字与类型一样的变量
static变量
1、在不同的cpp中可以定义名字与类型一样的变量
2、如果在.h中定义一个static成员,则所有include该文件的文件均拥有一份独立的该static成员,一个文件对其的修改不会影响到另一个文件
所以static变量一般是放在.cpp出现并定义.
例如
1.h
#pragma once
static int a = 5;
a.cpp
#include "1.h"
#include <iostream>
using namespace std;
void showstatic()
{
cout << "In a.cpp:" << a << endl;
a = 1;
cout << "In a.cpp:" << a << endl;
}
b.cpp
#include "1.h"
#include <iostream>
using namespace std;
void showstatic();
int main()
{
showstatic();
cout << "In b.cpp:" << a << endl;
system("pause");
}
static函数
在不同的cpp中可以定义函数原型一样的函数
类
不同的cpp中类的名字可以一样
类成员与函数
在.h中定义,所有成员必须在类中声明,在cpp中实现
非静态的常量整形数据成员不能就地初始化(*C++11中,标准允许使用等号=或者花括号{}进行就地的非静态成员变量初始化)
在类内部定义的成员函数将自动作为inline处理
在.h外部定义的函数需要加上inline说明
否则在被include多次时会出现重定义错误
1.h
#pragma once
#include <iostream>
class A
{
public:
void show();
};
void A::show()//无inline
{
std::cout << "hello" << std::endl;
}
a.cpp
#include "1.h"
#include <iostream>
using namespace std;
b.cpp
#include "1.h"
#include <iostream>
using namespace std;
error LNK2005: "public: void __thiscall A::show(void)" (?show@A@@QAEXXZ) 已经在 a.obj 中定义
类的const成员
在类中声明变量为const类型的成员不可以就地初始化
const常量的初始化必须在构造函数初始化列表中初始化,而不可以在构造函数函数体内初始化(*C++11中,标准允许使用等号=或者花括号{}进行就地的非静态成员变量初始化)
#pragma once
class A
{
public:
const int i=50;
};
error C2864: “A::i”: 只有静态常量整型数据成员才可以在类中初始化 d:我的资料库documentsvisual studio 2010projectsfasdfasd1.h 5 1 fasd
类的静态的数据成员
不可以就地初始化,需要到.cpp中进行定义
(对于非常量的静态成员变量,C++11与C++98保持了一致。需要到头文件以外去定义它)
类的静态的常量整形数据成员
可以就地初始化
如
class A
{
private:
const static int i = 5;
};
模板(不考虑export)
模板函数与模板类的声明与实现必须放在一个文件中
总结
是否可以在.h中定义 |
在不同.cpp中是否可以重名 |
特殊说明 |
|
---|---|---|---|
函数 |
不可以,会出现重定义错误 |
不可以 |
|
内联函数 |
可以 |
可以 |
为了确保所有调用该inline函数的文件中的定义一样,所以需要是在.h文件中定义 |
typedef |
---------------------- |
可以 |
|
常量 |
可以 |
可以 |
1、常量表达式进行初始化的,则可以在.h中声明与定义 2、非常量表达式进行初始化的,那么该变量应该在cpp文件中定义,而在.h文件中进行声明。 |
变量 |
不可以,会出现重定义错误 |
不可以(类型与名字) |
|
static变量 |
可以 |
可以 |
在.h中定义一个static成员,则所有include该文件的文件均拥有一份独立的该static成员,一个文件对其的修改不会影响到另一个文件 所以static变量一般是放在.cpp出现并定义. |
static函数 |
可以 |
可以 |
是否可以在.h中定义 |
是否可以就地初始化 |
特殊说明 |
|
---|---|---|---|
类 |
可以 |
||
类数据成员 |
------------------ |
不可以 |
(*C++11中,标准允许使用等号=或者花括号{}进行就地的非静态成员变量初始化) |
类成员函数 |
------------------ |
---------------- |
在.h外部定义的函数需要加上inline说明 否则在被include多次时会出现重定义错误 |
类const数据 |
------------------ |
不可以 |
1、在类中声明变量为const类型的成员不可以就地初始化 const常量的初始化必须在构造函数初始化列表中初始化,而不可以在构造函数函数体内初始化 2、同类数据成员中的特殊说明 |
类的静态的数据成员 |
------------------- |
不可以 |
不可以就地初始化,需要到.cpp中进行定义 (对于非常量的静态成员变量,C++11与C++98保持了一致。需要到头文件以外去定义它) |
类的静态的常量整形数据成员 |
------------------ |
可以 |
特殊说明 |
|
---|---|
模板 |
模板函数与模板类的声明与实现必须放在一个文件中 |
至于为什么会这样,与C++的编译和链接,和编译产生的目标文件(.obj),内部链接,外部链接有关,
我会在接下来的文章中向大家介绍
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 不想得手指关节炎?帮你提炼IDEA常用代码补全操作
- SpringBoot实现本地存储文件上传及提供HTTP访问服务
- 从 0 到 1 搭建技术中台之 iOS 可视化埋点实践
- 超给力,一键生成数据库文档-数据库表结构逆向工程
- 精讲RestTemplate第6篇-文件上传下载与大文件流式下载
- 【Rust日报】2020-09-12 Apple 使用 Rust 进行底层编程
- 精讲RestTemplate第4篇-POST请求方法使用详解
- 精讲RestTemplate第3篇-GET请求使用方法详解
- 通过JSch运行远程linux主机上的shell脚本
- 精讲RestTemplate第5篇-DELETE、PUT等请求方法使用详解
- 一个小小的签到功能,到底用MySQL还是Redis?
- 在IntelliJ IDEA中多线程并发代码的调试方法
- 使用java API进行zip递归压缩文件夹以及解压
- C#刷剑指Offer | 从上到下打印二叉树
- Java自动化测试(web自动化测试框架2 29)