C++初阶——类和对象(一)
一、面向过程和面向对象
1.面向过程
面向过程的程序设计(Procedure-Oriented Programming),简称POP,是一种是以程序执行流程为核心的编程范式。它是先分析出解决问题所需要的的步骤,然后用函数把这些步骤一步步地实现,在使用的时候依次调用就可以了。
2.面向对象
面向对象的程序设计(Object-Oriented Programming),简称OOP,关注的不再是完成一个任务的过程,而是完成这个任务涉及到的对象
。比如,在点外卖这一过程中,就涉及到用户,商家和骑手这几个对象。专业一点来讲,面向对象编程(OOP)以现实世界实体映射为核心,将问题域抽象为对象的集合,每个对象包含数据
和方法(函数
)的封装体。其哲学源于亚里士多德的“实体-属性”论,通过类(Class)定义对象的蓝图(设计图纸),实现“分类-实例化”的认知建模。
面向对象有三大特性:封装、继承、多态。
(1)封装
- 将
数据
和操作数据的方法(函数
)进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。 - C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使在
类(class)
中,通过访问修饰符(private
/public
)控制数据暴露程度,提升隐私性和安全性。 - 2024年波音787航电系统升级时,因多个函数共享
navigation_state
全局变量,导致姿态控制模块与通信模块数据冲突,引发短暂系统宕机;防止外部误操作:通过private
/public
修饰符,限制数据的直接访问,可以让银行账户的balance(余额)
字段仅允许通过withdraw()
方法修改;防御性编程:2024年特斯拉自动驾驶系统升级后,传感器数据的封装层拦截了12%的非法访问请求,避免了系统崩溃。 - 在C++初阶的类和对象内容,我们主要就是围绕封装这一特性展开。
(2)继承
- 继承(Inheritance)是面向对象编程中实现代码复用和层次抽象的核心机制,允许子类继承父类的属性和方法。这里不过多介绍,在C++进阶的内容,我们会详细讲解。
(3)多态
- 多态(Polymorphism)是面向对象编程中同一接口在不同上下文中表现不同行为的能力,其核心在于“一个接口,多种实现”。这也是非常重要的内容,同样,在C++进阶中,我们会详细讲解。
二、类的介绍
类定义了一个新的作用域,类的所有成员都在类的作用域中。通过class关键字将数据
与操作(成员函数
)绑定。在类体外定义成员时,需要使用 ::
域作用限定符指明成员属于哪个类域,比如在类里面声明一个成员函数,在类外面定义时,就要使用 ::
。
我们先来简单的感受一下:
class Stack
{
public:
// 成员函数
void Init(int defaultCapacity = 4)//提供一个缺省参数,如果没有传参,就使用这里的值
{
a = (int*)malloc(sizeof(int) * capacity); //基本操作,检查动态内存开辟是否成功
if (nullptr == a)
{
perror("malloc fail");
return;
}
capacity = defaultCapacity;
top = -1;
}
void Push(int x)
{
//...
a[++top] = x;
}
void Destroy()
{
free(a);
a = nullptr;
top = capacity;
}
private:
// 成员变量
int* a;
int top;
int capacity;
};
int main()
{
return 0;
}
在这里,我们主要是熟悉一下类的使用,没有详细实现具体的内容,关于栈,在数据结构专栏中已经进行了详细的讲解。在一个类中,重要的是访问限定符——public
、private
和protect,我们详细介绍一下:
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
如图所示:
struct(结构体)
在C语言中,里面是不可以定义函数的,但是在C++中,struct升级成了类,可以定义成员函数,但是struct里面的内容默认是公开的(public)
,外界可以随意访问,也是兼容了C语言结构体的性质,但class就不一样了:
class默认是私有,需要加上public声明才能被访问,如图:
允许外界访问的是函数func,至于成员变量a、b,则是不想公开的。 - 在这里,我们也可以在类里面给出函数的声明,在类的外面给出函数的定义:
这里需要指定函数Print是属于A这个类域。
我们来看下面一段代码:
这里的year=year含义就不清楚了,到底是形参自己赋值给自己还是将形参赋值给成员变量?因此我们应当这样写:
三、类的实例化
类定义仅仅是逻辑的蓝图,就像是一个房间的设计图纸,并不能真正的住进去,因此,我们需要对类进行实例化才会创建出具体的对象,分配内存空间,简而言之,用类创建对象的过程,称为类的实例化。同一个类可以实例化出多个不同的对象,比如:在特斯拉工厂的Robot类实例化后,每个机器人拥有了独立的关节状态;银行系统的Account类只有实例化后,才会绑定具体账户持有者。我们来举个例子:
class Stack
{
public:
// 成员函数
void Init(int defaultCapacity = 4)
{
a = (int*)malloc(sizeof(int) * defaultCapacity);
if (nullptr == a)
{
perror("malloc fail");
return;
}
capacity = defaultCapacity;
top = -1;
}
void Push(int x)
{
a[++top] = x;
}
void Destroy()
{
free(a);
a = nullptr;
top = capacity;
}
private:
// 成员变量
int* a;
int top;
int capacity;
};
int main()
{
Stack st1;
st1.Init(20);
st1.Init();
st1.Push(1);
st1.Push(2);
st1.Push(3);
st1.Push(4);
st1.Destroy();
return 0;
}
这里还是一个简单的栈,首先,我们实例化了一个栈st1,现在st1不是一个图纸,而是有具体内存空间的对象,我们可以对一个实例化出来的对象进行有实际意义的操作,比如初始化开空间(这里开的20,如果没有提供开多少的参数,也没关系,因为这里使用了缺省参数int defaultCapacity = 4
),元素入栈,栈的销毁等等,如果不进行实例化,是没有任何意义的,比如:
直接对着图纸插入数据,显然是没有任何意义的。
那么,我们刚才定义函数不是用了::
吗?
注意,这里是定义,其实还是在画图纸,并不是真正的使用。在类体外定义成员时,需要使用 ::
域作用限定符指明成员属于哪个类域,比如在类里面声明一个成员函数,在类外面定义时,就要使用 ::
。
还有一个问题,在链表中,我们特意强调了结构体是自定义类型
,struct的使用是要带上它的名字的,比如链表的结构体指针struct ListNode*
就是一个典型的例子,在C++中还需要这样吗?
答案是:不需要!
因为C++已经将结构体struct升级成了类,这是语法规定:
struct Stack
{
public:
// 成员函数
void Init(int defaultCapacity = 4)
{
a = (int*)malloc(sizeof(int) * defaultCapacity);
if (nullptr == a)
{
perror("malloc fail");
return;
}
capacity = defaultCapacity;
top = -1;
}
void Push(int x)
{
a[++top] = x;
}
void Destroy()
{
free(a);
a = nullptr;
top = capacity;
}
private:
// 成员变量
int* a;
int top;
int capacity;
};
int main()
{
Stack st1;
st1.Init(5);
st1.Init();
st1.Push(1);
st1.Push(2);
st1.Push(3);
st1.Push(4);
st1.Destroy();
return 0;
}
本期总结+下期预告
本期内容,我们正式进入C++初阶的类和对象部分,这是C++中非常重要的内容,下期内容继续为大家带来相关的知识!
感谢大家的关注,我们下期再见!