1. 写一个基本的类
上手C++以来,仅熟练使用if、for等基本语句的我有很多疑惑,本篇主要解答以下问题:
-
类是什么
-
public & private 的作用
-
创建一个类,.cpp和.h文件怎么写?
-
#ifdef # ifndef 有什么用
1.1 类是什么
数据结构是把一组相关的数据元素组织起来,然后使用它们的策略和方法。比如说树的定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
类,即自定义数据结构,TreeNode 就是一个类。 class 和 struct 的唯一区别在于默认访问权限不同。访问权限涉及到了类的一个主要特性——封装。
1.2 类的访问级别
对任何一个常见的类,calss关键字后接类名,类名后的大括号里会出现plublic、private和protected等关键字,即指出了此部分成员的访问级别。不指定访问说明符时,class 默认为 private ,而 struct 默认为 public。
class Lexer{
//private:
int lastChar = ' ';
string identifierStr;
double numVal;
public:
double GetNumVal();
string GetIndStr();
};
- 我们一般将接口写成public,类的接口包括用户所能执行的操作(构造函数和部分成员函数),以便于实例化一个对象后调用接口部分的提供的功能;
- 将实现写在private部分,包括负责接口实现的函数体,以及所需的各种私有变量和函数,这一部分的函数和变量是对象调用不了的,外界不了解的;
- 此外还有protected的声明,只能给class本身的函数以及从该class派生出来的子类使用(爸爸保护起来的就是要给儿子的)
类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程技术,封装即隐藏了类的实现细节,只有接口部分的成员可以在整个程序内被访问,通过访问说明符实现这种访问控制。
封装后的模块相对独立,类具体的实现细节可以自由修改,只要接口不发生改变,用户代码就无需改变, 同时用户代码也不能轻易破坏封装对象的状态。比如一家包子铺,其接口为用钱买包子,所有用户都只需要调用其接口,包子铺为提高效率更新生产包子的工具不影响用户访问,用户的行为也不会破坏其生产。
1.3 类的头文件 .h 与源文件 .cpp
在 CLion 中新建一个 C++ Class的时候,会自动创建一个 .h 头文件和一个 .cpp 源文件,这两个文件怎么写?
先说结论,将类的成员变量和成员函数的申明放在头文件中,成员函数的实现过程放在源文件中,使得头文件较为简洁,便于阅读和修改。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-csN1yi72-1601016026681)(…/…/figure/image-20200901152811165.png)]
如果要问为什么,这可能是工程实践的经验所形成的规范吧。我理解的是,当代码比较简单地时候我们定义了一个类,把class写在main.cpp里面,class函数定义在class大括号里面,也没什么问题。
但是如果这个类要经常在其他文件里复用呢?把代码复制过去显然不是一个好办法,所以我们可以把类放在单独的文件中,文件名与类名一致。写在一个文中,.cpp / .h / .cc 后缀其实差不多,当一个类成员函数很多的时候,比如看一看我们常用的string类,把声明和函数具体实现写在一起会很难阅读,所以我们又把声明放在了头文件中,实现放在源文件中,在源文件中#include头文件。
1.4 预处理功能 ifdef & ifndef
写好了头文件后,我们会在要使用该类的源文件中 #include 该头文件,#include就是一种预处理功能,预处理器会在程序编译以前用指定的头文件内容替换此命令。
我们还需要考虑一个问题,如果一个工程的几个文件中都使用了test类,那么就会#Include头文件多次,多次包含头文件可能导致重复定义等问题,使程序不能稳定运行。所以一般在头文件中会用到另一种预处理功能——头文件保护符。
#ifndef
检查一个变量是否被定义,若没有被定义则为真,#ifdef
相反。若为真,则执行后续操作直到#endif
为止,反之,忽略到#endif
部分代码。