【OOP】封装:类与对象
作者:互联网
引言
面向对象程序设计(OOP),最核心的概念就是 “对象” 。
简单地说,数据 + 函数 = 对象。
将一组数据和相关的函数结合起来,构成一个结合体,就是封装的过程。
类 & 对象
类(class)是一种用户自定义的类型,包含了数据和函数。
-
类中的数据 = 成员变量
- 在类内定义
-
类中的函数 = 成员函数
- 在类内声明
- 可以在类内 / 类外实现
class Person { // 自定义的类 public: string name; // 成员变量 void get_name () { // 成员函数 cout << "Name: " << name; } }
-
定义了类后,将类当成一个数据类型,去定义一个变量,这个变量就是类的对象。
class Person { ...... } Person a; // a 是 Person 类的对象 a.name = "Karry"; // 使用对象的成员变量的方法 a.get_name(); // 调用对象的成员函数的方法
通常,为了方便管理,我们会将不同的类放在不同的头文件和实现文件里:
-
头文件:成员变量、成员函数的声明
// 头文件 Person.h #fndef PERSON #define PERSON class Person { ...... } #endif
-
实现文件:部分成员函数的实现
// 实现文件 Person.cpp #include "Person.h" ...... // 部分函数实现
类的成员
类中的成员主要有:成员变量、成员函数
A)成员访问权限
访问权限分成三种等级:public、private 和 protected。
这里不详细说明三者的性质,只需简单理解,
public 的成员是可以在类外访问的,但是 private 的成员只能在类内访问。
class Person {
int age; // 默认的访问权限为 private
public:
string name; // public 访问权限
}
int main() {
Person a;
a.name = "Karry"; // name 是 public 的成员变量,可以在类外访问
// a.age = 23; // age 是 private 的成员变量,没有权限在类外访问
}
B)成员变量
成员变量的声明方式和普通的变量相同。
除了常见的数据类型,如:int
、float
、char
等,还有 auto
和 decltype
。
以下为 auto
和 decltype
的介绍,可适用于成员变量、成员函数、普通变量、普通函数等。
i. auto
特点:
- 必须在编译期确定类型
- 编译器会根据上下文自动确定变量的类型
使用的注意事项:
-
定义的同时进行初始化
-
如果是用来定义一连串的数据,这些数据必须为同样的数据类型
-
可用于:定义变量(尤其是复杂的类型)、追踪函数的返回类型
// 定义变量 auto num = 21; auto a = "Karry", b = "Wang"; // 追踪函数的返回类型 auto func(int a, int b) -> int { return a + b; }
-
不可用于:函数的参数
ii. decltype
特点:
-
可以对变量 / 表达式的类型进行推导
struct { string name; int id; } student; int main() { decltype(student) x; // 推导出 student 的类型,再用类型定义 x }
使用的方法:
-
结合
auto
和decltype
,可以自动追踪函数的返回类型auto func(int a, int b) -> decltype(a + b) { return a + b; } auto a = func(6, 9);
C)成员函数
成员函数的实现方式有:类内实现 / 类外实现
class Person {
public:
string name;
// 方法1:类内实现
void get_name() {
cout << "Name: " << name << endl;
}
}
// 方法2:类外实现
void Person::get_name() {
cout << "Name: " << name << endl;
}
定义在类内的成员函数,会被默认为内联函数,因此大多数情况下,我们会将类的实现和声明分开,便于管理。
有关内联函数的更多说明可以在后文的 “补充知识“ 中找到。
关于成员函数,有一些延伸的知识,可以点击后方链接,跳转到相关部分,如:
补充知识
1. 内联函数
内联函数(inline function)是一种特别的函数,可以在调用函数的地方生成与函数等价的表达式。例:
inline int min(int a, int b) {
return a < b ? a : b;
}
cout << min(2, 5) << endl;
以上代码等价于:
cout << (a < b ? a : b) << endl;
简单地说,内联函数就是将函数的实现直接 ”植入“ 我们调用函数的地方。
虽然,类似的,我们也可以用宏定义(define)来实现这个功能,但是内联函数和宏定义有着一定的区别。
内联函数(inline) | 宏定义(define) | |
---|---|---|
操作 | 在函数调用的地方生成等价的表达式 | 将宏定义的代码直接拷贝到目的地 |
功能 | 可以进行调试、执行检查 | - |
安全 | 不容易出错 | 容易出错 |
使用内联函数的注意事项:
- 内联函数的声明和实现不可以分开
- 如果函数包含大段代码 / 循环 / 复杂结构,避免使用内联函数
- 编译器有决定权,内联修饰符只是一个建议:
- 如果函数不值得内联,就会忽略内联修饰符
- 如果觉得一些函数需要内联,会将其自行转换为内联函数
- 定义在类内的成员函数都被默认为内联函数
- 构造函数、析构函数通常都是内联函数
- 大段 / 复杂的成员函数建议进行类外定义
2. 函数重载
函数重载:若干个函数有着同样的函数名(大概率功能是相似的),但是处理的数据信息不同。
使用函数重载需要注意的事项:
-
函数的参数类型 / 数量需要有变化
-
函数的返回类型 / 函数参数的名称不能作为函数重载的区分标识
-
如果函数参数有使用缺省值,要避免出现二义性
缺省值是函数参数中的默认值,例:
void print_num(int input = 2) { cout << "The number is " << input << endl; } print_num(5); // The number is 5 print_num(); // The number is 2
使用缺省值时要注意,把参数是缺省值的都往后放,避免混淆。
如果函数参数内有缺省值时,要注意避免二义性的使用,例:
void func(int a, int b = 1) { cout << "The sum is " << a + b << endl; } void func(int a) { cout << "The number is " << a << endl; } int main() { //func(5); // 这种情况下会导致函数重载失败,无法区分两个函数的使用 return 0; }
函数重载调用时的操作:
-
会优先调用类型匹配的函数,例:
int
的数据就优先用int
参数的函数 ...... -
如果没有类型匹配的函数,会进行内置类型转换,例:
int
的转去float
......void print_num(int a) { cout << "The number is " << a << endl; } print_num(5.2); // 会进行类型转换,输出 The number is 5
3. this 指针
类的成员函数,和普通的函数不同的是,它们可以使用类的成员变量。
这是因为,所有成员函数的参数中都有一个隐藏的指针变量 this
,指向当前对象,例:
void Person::get_name() {
cout << "The person name is " << this->name << endl;
}
标签:封装,函数,对象,成员,name,int,OOP,内联,变量 来源: https://www.cnblogs.com/bljw-02/p/16339575.html