前言:最近使用opencv,需要把c++复习下,发现改了好多这几年,随记录下这些概念和使用方法
一、头文件
作用:将可以被多个.cpp文件使用的函数或方法统一封装在一个文件中,当其他.cpp文件需要使用这个变量或函数时,通过#include将其包含进来就可以。
原因:这里涉及到c++的编译模式,头文件是不被编译的,只有源文件(.CPP)文件才会被编译,当然也可以说头文件和包含他的源文件一起编译,所以为了方便这些函数的多次复用和契合c++的分别编译,就可以将这些变量和函数放在.h文件中,谁需要用谁就#include下就可以了。
使用:头文件中写些变量声明了,函数声明了,类的定义了之类的,具体的可以查看文末引用的文献,那里面讲的详细。
引用:系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。用户自定义的头文件用双引号括起来,编译器首先会在用户目录下查找,然后再到c++安装目录下查找,最后在系统文件中查找。
二、源文件
.CPP文件,c语言中是.c文件,称为c++源文件,里面放的都是c++的源代码。.cpp文件里面的东西都是相对独立的,在编译(compile)时不需要与其他文件互通,只需要在编译成目标文件后在与其他的目标文件做一次链接(link)就行。比如,在文件a.cpp中定义一个全局函数“void a(){}”,而在文件b.cpp中需要调用这个函数。即使这样,文件a.cpp和文件b.cpp并不需要互相知道对方的存在,而是可以分别的对他们进行编译,编译成目标文件之后再链接,整个程序就可以运行了。
关于头文件和源文件,这里有两个名词,
声明:一般指头文件中的一些变量、方法、类的声明,即变量不赋值,函数不写方法体。
定义(实现):一般在说源文件中经常会出现这个词,就是对引入的头文件中的变量、方法、类等进行初始化,即给他们赋值,写上方法体。
源文件中引入头文件后,可以不用全部定义(实现)头文件中的所有成员(变量、函数、类),用到什么用什么就可以。
三、代码
3.1 目录结构
其中头文件中有三个文件,Complex.h、person.h、Test.h,对用三个名一样的源文件及一个main函数。
3.2 头文件声明
在写头文件时需要注意,有固定的样式,即如下方式
#ifndef _Complex_H_
#define _Complex_H_
… … // 声明、定义语句(你的代码写在这里)
#endif
Complex.h,里面声明了一个类,类中声明了一些变量和方法
#pragma once
#ifndef _Complex_H_
#define _Complex_H_
class complex {
private:
double real, imag;
public:
complex();//空的构造函数
complex(double,double);//默认参数的构造函数
void set(double,double);//设置函数
double getReal();//获取实部函数
double getImag();//获取虚部函数
double module();//获取复数模的函数
//复数加减乘除函数
complex add(complex);
complex decrease(complex);
complex multiply(complex);
complex divide(complex);
//复数显示函数
void show();
};
#endif // !_Complex_H_
说明:大家可能注意到了,我在Complex.h的开头使用了#pragma once,其实#pragma once和
#ifndef _SOMEFILE_H_
#define _SOMEFILE_H_
.......... // 一些声明语句
#endif
差不多。
#pragma once是一个比较常用的C/C++杂注,只要在头文件的最开始加入这条杂注,就能够保证头文件只被编译一次。#pragma once是编译器相关的,有的编译器支持,有的编译器不支持,具体情况可以查看编译器API文档,不过现在大部分编译器都有这个杂注了。
#ifndef,#define,#endif是C/C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式。
3.3 源文件实现
#include<stdio.h>
#include<cmath>
#include"Complex.h"
complex::complex()
{
real = 0;
imag = 0;
}
complex::complex(double a, double b) {
real = a;
imag = b;
}
void complex::set(double a,double b) {
real = a;
imag = b;
}
double complex::getReal() {
return real;
}
double complex::getImag() {
return imag;
}
complex complex::add(complex a) {
double real = this->real + a.getReal();
double imag = this->imag + a.getImag();
return complex(real,imag);
}
void complex::show()
{
if (imag >= 0)
{
printf("%.2f+%.2fi", real, imag);
}
else
{
printf("%.2f%.2fi", real, imag);
}
return;
}
这里笔者没有将Complex.h中类的所有函数都实现,然后有个比较有意思的事情,去Complex.h文件中看下
有些函数有些绿色波浪线,鼠标靠上去后显示,这个函数没有定义,就是我们没有在引用它的源文件中实现它。
我们点击最下面的显示可能的修补程序,然后点击第一个创建“函数名”的定义(在Complex.cpp中),这时候,编译器就帮我们在引用该头文件的源文件(本文中指Complex.cpp)中将其实现了,然后就没有绿色波浪线了。
四、说明
源文件中实现引用的头文件中类大体就是这样的,对于函数:返回类型 类名::函数名
五、踩坑
5.1、使用没有被源文件实现的函数
main函数中使用 Complex.cpp
#include"Complex.h"
#include<stdio.h>
int main() {
complex A;//验证默认构造函数
printf("A为:");
A.show();
printf("\n");
complex B(2, -1);//验证两个参数构造函数
printf("B为:");
B.show();
printf("\n");
}
如果,我们在main函数中使用complex类,然后Complex.cpp源文件中有个函数并没有被实现,这就会导致错误,比如,笔者最开始并没有在Complex.cpp中实现show()函数,然后直接就在main函数中,如上图,F5一运行就报下面的错误
在Complex.cpp文件中实现show()方法后再运行,就没啥事了。
5.2 源文件中包含了头文件,就一定要将头文件中声明的函数实现吗?
并不需要,笔者并没有在Complex.cpp实现所有Complex.h中complex类的所有函数,一样可以正常运行。
5.3 我们看到,complex类是写在Complex.h中,这很容易导致误解,以为这个头文件名和这个类名要一样,其实不是,比如写一个util.h头文件,里面可以包含word类,excel类,ppt类等。