首页 > 代码库 > 类和封装的概念
类和封装的概念
类和封装的概念
什么是类?
电脑一般而言是由CPU,内存,主板,键盘和硬盘等部件组合而成。
思考:学习电脑组装需要多少时间?学习电脑组装是都需要学习显示器,键盘,鼠标,主板等部件的设计与制造?
类的封装
我们可以把程序按某种规则分成很多"块",类与类之间可能会有联系,每个类都有一个可变部分(public)和一个不可变部分(private)。我们需要把可变部分和不可变部分分离出来,将不可变的部分暴露给其他类,而将可变的部分隐藏起来,以便于随时可以让它修改。这项工作就是封装。
类分为以下两个部分
- 类的实现细节
- 类的使用方式
类为什么需要封装
- 当使用类时,不需要关心其实现细节。当我们创建类时,才需要考虑其内部实现细节。
例如:普通用户使用手机,只需要学习如何发短信,打电话,拍照等等。而这些功能如何实现,则是手机开发工程师需要考虑的事情。
- 高内聚,低耦合移植时我们所追求的,用好封装恰恰可以减少耦合
- 只要对外接口不改变,可以任意修改内部实现,这个可以很好的应对变化。
- 类具有了简洁清晰的对外接口,降低使用者的学习过程。
如何将一个类封装
- 根据经验:并不是类的每个属性都是对外公开的
- 如:女孩子不希望外人知道自己的体重和年龄
- 如:男孩子不希望别人知道自己的身高和收入
- 而一些类的属性是对外公开的
- 如:人的姓名,学历,国籍等
- 必须在类的表示法中定义属性和行为的公开级别
- 类似文件系统中文件的权限
C++中类的封装
- 成员变量:C++中用于表示类属性的变量
- 成员函数:C++中用于表示类行为的函数
- C++中可以给成员变量和成员函数定义访问级别
- public
成员变量和成员函数可以在类的内部和外界访问和调用
- private
成员变量和成员函数只能在类的内部被访问和调用
示例代码:类成员的访问属性
struct Human { void sleep() { printf("I‘m sleeping...\n"); }
void work() { printf("I‘m working...\n"); } };
struct Girl : Human { private: int age; int weight; public: void print() { age = 22; weight = 48;
printf("I‘m a girl, I‘m %d years old.\n", age); printf("My weight is %d kg.\n", weight); } };
struct Boy : Human { private: int height; int salary; public: int age; int weight;
void print() { height = 175; salary = 9000;
printf("I‘m a boy, my height is %d cm.\n", height); printf("My salary is %d RMB.\n", salary); } };
int main() { Girl girl; Boy boy; Human human;
human.sleep();
girl.print();
boy.age = 19; boy.weight = 120; boy.height = 180; //Error: ‘int Boy::height‘ is private
boy.print();
boy.work();
return 0; } |
分析:
- 从我们通过boy.height = 180;修改boy对象中的身高的值时,编译时会产生错误。提示我们Boy::height这个成员变量是私有的。但是我们却可以在boy.printf()函数中修改height的值。所以我们可以知道:private的成员只能在类的内部被访问和调用
- 我们通过boy.age = 19;编译时并没有发生错误可以知道。类的public成员可以在类的外部修改,所以public的成员可以在类的内部和外界访问和调用
- 我们在Human这个类中没有明显定义类的成员属于private还是属于public,但是我们在main中调用它编译没有报错。说明struct定义的类默认为public的。
- 我们可以在boy中使用Human类中的函数work();
类成员的作用域
我们知道全局变量和局部变量都有作用域,那么类也有作用域吗?
示例代码:类成员的作用域
#include <stdio.h>
int i = 1;
struct Test { private: int i;
public: int j;
int getI() { i = 3;
return i; } };
int main() { int i = 2;
Test test;
test.j = 4;
printf("i = %d\n", i); // i = 2; printf("::i = %d\n", ::i); // ::i = 1; // printf("test.i = %d\n", test.i); // Error printf("test.j = %d\n", test.j); // test.j = 4 printf("test.getI() = %d\n", test.getI()); // test.getI() = 3
return 0; } |
分析:
- 我们单纯打印printf("i = %d\n", i); i的变量是main函数内定义的i的值,而不是全局变量i=4的值。这是因为小范围作用域的值会覆盖大范围作用域的值。
- 我们使用::作用域解析运算符来使用默认全局变量i的值。
- 我们访问Test类中成员时,不能从外部直接访问类的成员,公有成员变量和成员函数也是不能直接访问,需要通过对象才能访问类的成员。如代码所示:访问类成员i变量,需要定义一个test对象,通过test对象才能访问i的值。
- 类成员的作用域都只在类的内部,外部无法直接访问。也就是说不同类中使用相同的类成员名不会发生冲突。实际上类与命名空间有点相像。
类和封装的概念