C++是一门面向对象的语言。C++刚出现的时候,被称为”C with Classes”,可见类之于C++的重要性。在本章我们将关注C++的类及其用法。

引言:C语言的结构体

C语言中,我们学习了结构体。使用结构体可以把不同类型的变量组合在一起。

1
2
3
4
5
6
struct MyStruct{
int a;
char b;
float c;
};
struct MyStruct mystruct;

使用结构体,要分清下面几个概念:

  1. 结构体名MyStruct是结构体的名字。
  2. 结构体类型struct Mystruct是一个结构体类型
  3. 结构体变量struct MyStruct类型的变量mystruct是一个结构体变量。

为了避免每次定义结构体变量的时候都要写 struct MyStruct ,我们常常采用 typedef 的方式将结构体名升级为类型名:

1
2
3
4
5
6
typedef struct MyStruct{
int a;
char b;
float c;
}MyStruct;
MyStruct mystruct;

通过typedef ,我们将结构体类型 struct MyStruct 命名为 MyStruct ,这样定义结构体变量的时候就不用多写struct 了。

但是,使用typedef只是权益之计。我们基本上不会用到结构体名,所以更希望定义的结构体直接就是一个类型,这样更加简洁。C++中的类解决了这个问题。类与结构体有着千丝万缕的联系

C++当然不是因为这个原因而引入类。但是从这个问题里我们可以看出C和C++的不同理念:C语言注重程序运行的速度和占用空间的最优;C++牺牲了一部分运行速度以换取程序的简洁与编程的效率。


类与结构体

C++的结构体

C++遵循简洁性,结构体的定义比C语言更加简单,定义的结构体名可直接作为类型名使用:

1
2
3
4
5
6
struct MyStruct{
int a;
char b;
float c;
};
MyStruct mystruct;

同时,结构体内部的成员不仅可以是变量,还可以是函数,如下图:

访问结构体中的函数和访问其中的变量的方法相同,都是结构体变量.成员 的方式。

类的引入

为了保持和C语言的兼容性 ,C++采用类来代替结构体 ,同时C语言中的结构体在C++中依然保留。

如果不这样做的话,随着C++的发展,C++的结构体可能会与C语言的结构体不兼容,C语言的结构体拿到C++中就可能出错或与预想结果不符。

所以类类型的定义应该看起来是下面的样子(和结构体差不多):

1
2
3
4
class MyClass{
//成员变量
//成员函数
};//不要忘了分号!

再强调一遍,不要忘了分号!

结构体与类的区别

  1. 下面我们会知道,类的成员的访问默认是私有的,在类外不能访问到。

  2. 而C++的结构体为了保持与C语言中结构体的兼容性 ,它的成员默认是公有的,可以在结构体外访问到。想想为什么?

  3. 本质上,C++的类就是结构体 ,或者说结构体其实就是一种类

类的访问控制及定义方式

类的访问控制

C++使用publicprivateprotected 这三个关键字来控制用户对类成员的访问权限。这三个关键字称为段约束符

  1. 使用public 修饰的成员,在类外是可以访问到的。public成员常常作为类对外部的访问接口
  2. 使用private 修饰的成员,在类外是不能访问的,否则编译器会报错 xxx is private within this context 。它只能在类内访问,是开发者有意隐藏,不希望用户直接访问到的部分
  3. 使用protected 修饰的成员,可以在子类(派生类)中被访问到,除此之外内外则不能访问。子类的概念将会在继承部分给出。

类的定义方式

结合类的访问控制,我们给出类规范的定义方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyClass{
public:
void init(int a,char b){
_a=a;
_b=b;
}
int show(){
return add();
}
private:
int _a;
char _b;
int add(){
return (_a+_b);
}
};

说明

  1. 建议在类的成员变量前面加一个下划线 。否则,以init()函数为例,就会出现 a=ab=b的情况,很不自然。而加下划线就可以将它们区分,同时类的成员变量的有下划线,也有助于我们对类成员的识别,提高程序可读性。

    而函数没有这个冲突,所以可加可不加。但为了提高可读性也建议加上。不想打不加也行

  2. 关于缩进,建议publicprivateprotected顶格,之间的代码离最左边一个Tab。个人认为这样代码美观一些。反正这是提高可读性的一种方式,怎么好看就怎么来。

类的实现

上面的例子,类的成员函数是放在类内实现的。

一般地,类的成员函数,既可以在类内实现,也可以在类外实现

类外实现

我们在类内声明了一个函数,在类外实现这个成员函数的时候,要加上类名和访问限制修饰符:: 。还是以之前的类为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyClass{
public:
void init(int a,char b){
_a=a;
_b=b;
}
int show();
private:
int _a;
char _b;
int add();
}
int MyClass::show(){
return add();
}
int MyClass::add(){
return (_a+_b);
}

在类外定义访问权限为private 的函数与它不能在类外被访问到并不矛盾。

类内实现

类内实现函数的比较简单,所以此处不再赘述。

要注意的是,类内实现的函数,编译器会判断是否要将其优化为内联函数! 如果该函数没有循环语句和递归语句,则编译器会将其识别为内联函数,相当于自动在它前面加上了inline关键字

要证明这一点很简单,只需要观察汇编代码中是否有call指令即可。

总结

类的成员函数的实现

  1. 成员函数的函数体比较短,建议放在类内实现。这样编译器会自动将其优化为内联函数,加快程序运行的速度。
  2. 成员函数的函数体比较长,建议放在类外实现。这样会避免类的代码过长,使得程序可读性降低。
  3. 类外实现的函数,必须将函数的声明和定义放在同一个文件中,或类放在头文件中,定义(实现)放在cpp文件中。即定义要找得到声明
  4. 成员函数在类外实现是更常见的方式。

类与面向对象

类的实例化

我们可以把类看成一个房屋的图纸,它规定了房屋应该怎么修建。但是,它没有真正地修好这个房屋。要建出这个“房屋”,还要进行实例化。

类的实例化,就是定义一个类类型的变量。 比如我已经定义了名叫MyClass的类类型,我们再定义一个类类型的变量:MyClass myclass ,这就是类的实例化。 而对象就是类的实例。

面向对象的理解

面向对象编程,即OOP(Object Oriented Progra)。它是C++的核心。

面向对象和面向过程有什么区别呢?

它们都是认识世界的方式,但角度不同。以进浴室洗澡为例,

  • 打开浴室门
  • 打开水龙头
  • 洗澡
  • 擦身体
  • 对于门这个对象,它的方法是开门这个动作。我们传递力的大小的参数给门,如果满足的话就打开。
  • 水龙头同理,调用放水这个方法。
  • 人调用洗澡这个方法。
  • 毛巾调用吸水的方法。
结论

面向过程一直以人的视角来完成事件;而面向对象则是不断切换角度和观察对象,调用所关注对象的某个方法来完成某个事件,然后依靠参数的传递完成对象之间的交互