使用结构体有一个问题,可以在结构体外部任意获取和修改其成员变量,带来了很大安全隐患,为此 C++ 引入了一个全新的概念,类!类相比结构体,除了有成员变量外,还可以定义成员函数,调用者通过成员函数来获取或修改成员变量,以达到保护的作用;当然类还有其它很多的功能和好处,其中一点就是面向对象的思想。
结构体
C 语言中使用结构体 struct
来实现可以包含多个数据成员的复杂数据结构。
1 | struct Stu |
使用结构体的时候需要注意几点:
- 结构体定义不要忘了最后的逗号,声明语句也是一条语句,每条语句都必须以逗号结束;
- 使用结构体的时候要写完整的类型名称
struct Stu
,省略struct
关键字会报编译错误unknown type name ‘Stu’; use ‘struct’ keyword to refer to the type
; - 如果要省略
struct
关键字,可以使用类型重命名关键字来结构体类型起个别名,如typedef struct Stu Stu;
; - 可以把声明语句和重命名语句合二为一,在结构体定义的时候就给它一个别名。
1 | typedef struct Stu |
C++ 中可以不用使用
typedef
重命名而直接省略struct
关键字,但在 C 中不行。
完整代码示例:struct.c
类
使用结构体有一个问题,可以在结构体外部任意获取和修改其成员变量,带来了很大安全隐患,为此 C++ 引入了一个全新的概念,类!类相比结构体,除了有成员变量外,还可以定义成员函数,调用者通过成员函数来获取或修改成员变量,以达到保护的作用;当然类还有其它很多的功能和好处,其中一点就是面向对象的思想。
C++ 中的结构体也支持成员函数定义,但在 C 中不行;即使如此,结构体的功能也很有限,只在比较简单的情形下使用,遇到复杂的需求还是得使用类来实现。
1 | class Student |
这个例子中使用 setName
成员方法来修改成员变量 name
,但同样也可以直接访问修改,因为其成员变量也是公有的,那这就起不到保护的作用了,这时候就需要使用访问修饰符 access sepcifier。
完整代码示例:firstclass.cpp
访问修饰符
类的访问修饰符有三个:public, private, protected
。
public
修饰的成员是公有的,无论是类里面还是类外面都可以直接访问;private
修饰的成员是私有的,只有在类里面才能访问,类外面访问是非法的;protected
修饰的成员可以在类里面和它的子类里面访问,类外面访问是非法的。
上一个例子中,类的所有成员变量和成员方法都被声明成 public
,所以起不到保护的作用,比较好的做法是把成员变量声明成私有的,成员方法声明成公有的,这样在类外面就只能通过成员方法来访问修改成员变量。
1 | class Student |
如果没写修饰符,则默认为 private
,所以本例中的 private
关键字可以省略,三个成员变量同样是私有的,只是一般不会省略,这样可读性会强一点,所有私有的成员和公有的成员一目了然。
1 | class Student |
如上例所示,如果整个类都没有修饰符,则所有成员变量和成员方法都是私有的,这时候无论是通过 stu.name
还是 stu.getName()
来访问都是非法的,一般都不会这么写的,因为类定义出来就是使用的,定义一个“全私有”的类是毫无意义的。
完整代码示例:access-attribute.cpp
内联函数
类的成员方法在类里面声明,方法定义则可以在类里面也可以在类外面,如果在类里面定义则是一个内联函数,在类外面定义也可以通过关键字 inline
将其定义成内联函数。内联函数在编译的时候会使用函数体替换函数调用,优点是执行速度快(减少了函数调用的开销),缺点是代码体量变大,特别是调用函数体比较大或者调用次数多的时候。一般在函数体较小的地方使用内联函数,比如成员变量的 Getter 方法。
1 | class Student |
这个例子中 getName
定义在类里面,setName
定义在类外面但加了 inline
关键字,所以这两个函数都是内联函数,而 printInfo
定义在类外面且没有 inline
关键字,所以不是内联函数。
完整代码示例:inline.cpp
多文件
在前面的例子中类的声明和实现放在一个文件里,更好的做法是把声明和实现分开,分别放在头文件 *.h/*.hpp
和实现文件 *.cpp/*.cxx
中。
避免头文件被重复包含
在头文件中要声明只能被包含一次,避免其被多个文件重复包含,有 2 个方案:
- 在头文件的第一行加上
#pragma once
; - 通过宏定义来检查,如下:
1 |
|
#include <> “”
#include <>
引用的是编译器的类库路径里面的头文件;#include ""
首先在当前路径查找头文件,如果找不到再去编译器的类库路径找,当前路径是指 .cpp 文件所在的路径,不是工程的根目录,如果 .h 文件和 .cpp 文件不在一个目录,比如:
1 | |-project |
则在 test.cpp 中要写 #include "../include/test.h"
才能正确引用 test.h 文件。
一般引用系统类库中的头文件用 <>,引用自己工程的头文件用 “”,虽然 “” 也可以引用系统类库头文件,但会先查找当前路径,找不到再去系统类库查找,其效率当然不如直接写 <>。
使用 cmake
单个文件可以使用 gcc/g++
编译器直接编译链接,如 g++ test.cpp
,但多文件的时候就得借助其它工具了。除了使用像 vs 之类的大型 IDE 之外,还可以使用开源、跨平台的工具集 cmake
。
首先,我们新建一个 build
目录用于存放生成的中间文件和可执行文件,当前的目录结构如下:
1 | |-project |
然后编辑配置文件 CMakeLists.txt
,注意这个文件名不能修改,否则 cmake
无法识别。
1 | # define cmake required version |
接下来开始使用 cmake
来生成我们的工程,首先进入 build
目录,因为要让文件生成在这里,然后执行 cmake <path>
,path
为 CMakeLists.txt
所在的目录,之后生成对应的配置文件和中间文件,最后再执行 make
命令进行编译链接。完整的命令如下:
1 | $ cd build |
完整代码示例:multi-files