一、前言
关于 struct 与 class,相信大家或多或少有些了解,本篇的目的是让大家完全透彻的熟悉,不在是片面了解。
二、基础
2.1、起源
大家学过 C 语言,也应该学过 C++,先帮大家从底层先梳理一下基础知识:
- struct 与 class 的出现
- C 语言中是有 struct 结构体的,但是 C 语言是没有 class 的;
- C++,扩展了 struct,并有了 class 的出现;
- struct 与 class 的定义
- C 语言中,struct 只是数据结构,没有函数;
- C++ 中,struct 与 class 类似,可以有变量,可以有函数;
我们不谈 C 语言,因为它没有 class,struct 也功能单一。
2.2、C++ 中两者的区别
C++ 中 struct 与 class 的区别:
- 成员:class 中,成员默认是 private 的,而 struct 中,成员默认是 public 的;
- 继承:class 默认是 private 继承,而 struct 默认是 public 继承;
- 模板:class 可以使用,而 struct 不能;
2.2.1、继承
关于继承,可能大家都忘记了『基类与子类在继承时,属性的限制』:
public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限。
public继承方式
基类中所有 public 成员在派生类中为 public 属性;
基类中所有 protected 成员在派生类中为 protected 属性;
基类中所有 private 成员在派生类中不能使用。protected继承方式
基类中的所有 public 成员在派生类中为 protected 属性;
基类中的所有 protected 成员在派生类中为 protected 属性;
基类中的所有 private 成员在派生类中不能使用。private继承方式
基类中的所有 public 成员在派生类中均为 private 属性;
基类中的所有 protected 成员在派生类中均为 private 属性;
基类中的所有 private 成员在派生类中不能使用。
综上,我们可以得出结论:
- 基类成员在派生类中的访问权限不得高于继承方式中指定的权限;
- 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用;
- 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;
- 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected;
我们用图来方便大家记忆:
inherience.png2.2.2、模板(template)
说起模板可能大家有点生疏,但我换个词大家就能理解:函数重载!
何谓函数重载?
函数重载针对的是形参(即入参),可以有多个同名函数,但只要满足以下条件中的一个就行:
- 函数的参数个数不同;
- 函数的参数类型不同;
- 函数的不同参数类型顺序不同;
例如我有如下方法:
//交换 int 变量的值
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
//交换 float 变量的值
void swap(float *a, float *b){
float temp = *a;
*a = *b;
*b = temp;
}
如果用模板来实现如下:
template<typename T> void Swap(T *a, T *b){
T temp = *a;
*a = *b;
*b = temp;
}
template是声明模板关键字,typename是声明类型关键字(Java中泛型);
2.3、类型区别
- struct 是值类型(可以简单类比为基本类型),因此,是值拷贝传递,数据的改变不会相互影响;
- class 是引用类型(可以理解为指针),也叫对象,因此,是引用传递(指针传递),数据的改为会相互影响;
最简单的理解:
- 内存地址是一致还是发生变化(struct 是值传递,所以是新的变量;而 class 是引用传递,内存地址不变);
- 内存地址的引用计数是否发生变化(struct 没有引用计数;而 class 会随着引用传递计数不断增加);
三、Swift 中的两者浅析
无论是 OC 还是 Swift,它们的底层都是 C++,所以,struct 与 class 在 C++ 层面的特性是不会发生变化的,我们主要讨论的是两者在 Swift 层面的相同点与不同点。
3.1、相同点
- 定义属性用于存储值
- 定义方法用于提供功能
- 定义下标操作用于通过下标语法访问它们的值
- 定义构造器(init)用于设置初始值
- 支持扩展(extension)以增加默认实现之外的功能
- 遵循协议(protocol)以提供某种标准功能
3.2、不同点
- 继承允许一个类继承另一个类的特征
- 类型转换允许在运行时检查和解释一个类实例的类型
- 析构器允许一个类实例释放任何其所被分配的资源
- 引用计数允许对一个类的多次引用
以上主要是针对 class 来说的,struct 是没有这些的。
四、总结
Swift 之所有增加了 struct,主要有以下几点考虑:
- 安全性:值拷贝传递,没有引用计数;
- 内存:没有引用也就不会因为循环引用导致内存泄漏;
- 速度:值类型是直接在栈上分配,而不是在堆上,所以比 class 要快很多;
- 拷贝:值类型是深拷贝,class 默认浅拷贝;
- 线程安全:自动线程安全的(无论哪个线程去访问都非常简单);
关于速度话题的延伸:
- “堆”和“栈”并不是数据结构上的Heap跟Stack,而是程序运行中的不同内存空间;
- 栈是程序启动的时候,系统事先分配的,使用过程中,系统不干预;
- 堆是用的时候才向系统申请的,用完了需要交还,这个申请和交还的过程开销相对就比较大了;
- 栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快;
从两方面来考虑:
- 分配和释放:堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,而栈却不需要这些;
- 访问时间:访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次;
关于速度,这里有一个早期 stackoverflow 的帖子:
http://stackoverflow.com/a/24243626/596821
速度的测试用例(Github):
https://github.com/knguyen2708/StructVsClassPerformance
苹果官方也给出了两者如何选择使用:
https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes
说白了就是:
- 不考虑应用的状态,也不存在对外的数据交互,就用 struct;
- 需要在多个地方修改 / 更新数据(共享数据),就用 class;
- 涉及到继承就用 class,没有就用 struct;
- 官方建议用 struct + protocol 来建模继承与共享;
网友评论