从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(上)

简介: 从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针

       前面提到C++是面向对象的语言,但不是纯面向对象,因为要兼容C语言,

所以C++可以面向对象和面向过程混编,像Java是纯面向对象的语言,只有面向对象,

就算你想实现一个排序也要写一个类出来……

本章将正式开始学习C++中的面向对象。

1. 面向对象

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

C++是基于面向对象编程(Object Oriented Programming,OOP),关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。举个栗子,比如设计简单的外卖系统:

面向过程:关注实现下单、接单、送餐这些过程。体现到代码层面 -- 方法/函数

面向对象:关注实现类对象及类对象间的关系,用户、商家、骑手以及他们之间的关系。

体现到代码层面 —— 类的设计及类之间的关系。

1.1 类的引入

在C语言中,结构体中只能定义变量,而在C++中,结构体内不仅可以定义变量,还可以定义函数。因为在 C++ 里,struct 也跟着升级成了类。

因为 C++ 兼容 C 里面结构体的用法,所以 C++ 就可以直接使用类名来定义了:

struct Student 
{
  char name[10];
  int age;
  int id;
};
int main()
{
  struct Student s1;  // 兼容C
  Student s2;         // C++就可以直接使用类名,Student类名,也是类型。
 
  strcpy(s1.name, "小明");
  s1.id = 10001;
  s2.age = 20;
 
  strcpy(s2.name, "小红");
  s2.id = 10002;
  s2.age = 19;
 
  return 0;
}

既能用 struct Student s1 来定义,还能直接使用 Student s2,通过使用类名直接定义。


这体现了 C++ 兼容 C 的特点。


但是如果这是在 C语言 里, stuct Student 才是它的类型,


直接使用 Student 定义是不可以的。


它其实就是一个结构,可以理解成和之前学的结构体是 "一样的" ,只是定义的方式既兼容了 C 还兼容了 C++ ,下面我们还会认识到一些它的不同之处。


如果是在C语言里,结构体里只能定义变量,就是一个多个变量的集合。


如果我们想要将 s1 中的变量进行初始化,还得一个个写,很麻烦,

但是在C++里,不仅可以定义变量,还可以定义函数(方法)

定义一个 "初始化" 函数:

struct Student 
{
    /* 成员变量 */
  char name[10];
  int age;
  int id;
 
    /* 成员方法 */
  void Init(const char* name, int age, int id) {
    ...
  }
};

在 C++ 中一般称这些变量为成员变量,称这些函数为成员方法

这个时候似乎发现了一些新的问题,这个成员方法的参数名取的好像和成员变量里一样了,

比如访问 name 的时候到底是成员变量里的 name 还是成员方法里的 name 呢?

这就让人区分不开了,为了能够更好的区分哪个是成员变量,我们在定义成员变量名时可以给它们做一些标记:下面是几种常见的表示为成员变量的 "风格" :

① 前面加斜杠 :

char _name[10];

② 后面加斜杠:

char name_[10]

③ 前面加个 m (表示成员 member):

char mname[10]


这个并没有明确的规定,不同的公司也有不同的风格。本博客常用第一种风格

这样就可以区分开来了:

struct Student 
{
  /* 成员变量 */
  char _name[10];
  int  _age;
  int  _id;
 
  /* 成员函数 */
  void Init(const char* name, int age, int id) 
    {
    strcpy(_name, name);
    _age = age;
    _id = id;
  }
};

为了方便测试,再来写一个简单的打印函数,调用它们进行一个打印:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
struct Student 
{
  /* 成员变量 */
  char _name[10];
  int  _age;
  int  _id;
  /* 成员函数 */
  void Init(const char* name, int age, int id) 
  {
    strcpy(_name, name);
    _age = age;
    _id = id;
  }
 
  void Print() 
  {
    cout << _name << " " << _age << " " << _id << endl;
  }
};
 
int main()
{
  struct Student s1;
  Student s2;
 
  /* 初始化 */
  s1.Init("小明", 20, 10001);
  s2.Init("小红", 19, 10002);
 
  /* 打印 */
  s1.Print();
  s2.Print();
 
  return 0;
}

总结:C++ 对我们的 struct 进行升级了,升级为类了。它兼容以前的用法,又有了新的用法。


1.2 class 关键字

刚才引入部分讲了 struct ,知道了它在 C++ 里升级成了类。其实 C++ 也有自己的亲儿子,就是 class,class语法和struct一样,注意类定义结束时后面要加分号。


但我们把上面代码的struct改成class居然报错了,这又是为什么呢?因为 C++ 讲究 "封装" ……C++ 这里是它把数据和方法都放在了类里面。

这和C语言是不同的,C语言里数据是数据,方法是方法。

这里我们就来提一下 面向对象OOP的三大特性:封装、继承、多态。

我们先来重点看一下这个 封装

① 数据和方法都被放在了一起。

② 访问限定符

就是因为这个访问限定符,所以这里我们报错了,下面来学习一下访问限定符。


2. 类的访问限定符及封装

2.1 访问限定符

C++ 实现封装的方式:用类将对象的属性与方法结合在一起,让对象更加完善,

通过访问权限选择性地将其接口提供给外部的用户使用。

一共有三种访问限定符,分别是 public(公有)、protected(保护)、private(私有)。

顾名思义,公有就是随便访问,保护和私有就是不让你随便访问得到。

访问限定符说明

① public 修饰的成员,可以在类外面随便访问(直接访问)。

② protected 和 private 修饰的成员,不能在类外随便访问。

 (此处 protected 和 private 是类似的,现在你可以认为他们是一样的,后面我们讲继承的时候才能体现出它们两的区别


这就分出了两个阵营,一个阵营是可以随便访问的,一个阵营是不能随便访问的。

③ class 的默认访问权限为 private,struct 为 public !

这就是为什么我们刚才编译会报错,因为 class 默认访问权限是 private!

那好,既然知道问题所在了,该如何解决让它成功访问呢?

使用访问限定符,加一个 public :

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Student
{
  /* 成员变量 */
  char _name[10];
  int  _age;
  int  _id;
public:
  /* 成员函数 */
  void Init(const char* name, int age, int id) 
  {
    strcpy(_name, name);
    _age = age;
    _id = id;
  }
 
  void Print() 
  {
    cout << _name << " " << _age << " " << _id << endl;
  }
};
 
int main()
{
  struct Student s1;
  Student s2;
 
  /* 初始化 */
  s1.Init("小明", 20, 10001);
  s2.Init("小红", 19, 10002);
 
  /* 打印 */
  s1.Print();
  s2.Print();
 
  return 0;
}

成功运行,我们再来细说一下刚才加进去的访问限定符。

③ 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。

④ 如果后面没有访问限定符,作用域就到 { (最外面花括号)类结束。

也就是说,我们刚才加进去的 public ,

从它开始到下一个访问限定符出现为止的这块范围,都是共有的了,

但是因为后面没有再次出现访问限定符,所以作用域就到类结束为止。


再加一个访问限定符 private 进去看看:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Student
{
  /* 成员变量 */
  char _name[10];
  int  _age;
  int  _id;
public:
  /* 成员函数 */
  void Init(const char* name, int age, int id) 
  {
    strcpy(_name, name);
    _age = age;
    _id = id;
  }
private:
  void Print() 
  {
    cout << _name << " " << _age << " " << _id << endl;
  }
};
 
int main()
{
  struct Student s1;
  Student s2;
 
  /* 初始化 */
  s1.Init("小明", 20, 10001);
  s2.Init("小红", 19, 10002);
 
  /* 打印 */
  s1.Print();
  s2.Print();
 
  return 0;
}

现在, public 能影响到的范围就到 private 出现前为止了,然后在main里的打印函数就会报错。

这就是访问限定符在这里起到的一个作用。

注意事项:

① 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。

② 一般在定义类的时候,建议明确定义访问限定符,不要用 struct / class 的默认的访问权限

class Student 
{
private:
  char _name[10];
  int  _age;
  int  _id;
 
public:
  void Init(const char* name, int age, int id) 
    {
    strcpy(_name, name);
    _age = age;
    _id = id;
  }
  void Print() 
    {
    cout << _name << " " << _age << " " << _id << endl;
  }
};

虽然不指定会有默认限定,但是还是建议明确写出来,

因为这样能让他人一眼就看出它是共有的还是私有的。


2.2 封装的意义和本质

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来实现对象进行交互。

① 把数据都封装到类里面。

② 可以给你访问定义成公有,不想给你访问的定义成私有或者保护。

封装的意义

封装的意义是什么?

封装是一种更好的严格管理,不封装是一种自由管理。

那么是严格管理好,还是自由管理好呢?

举一个疫情防控的例子:

某国单日新增一百万,所以是自由的管理好呢?还是严格的管理好呢?

我们和某国其实都是在控制疫情的,但是我们是严格的管理,控制疫情。


封装的本质

       封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用 户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日 常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

       对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计 算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以 及键盘插孔等,让用户可以与计算机进行交互即可。

       在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

类也是一样,我们使用类数据和方法都封装到了一起,不想让人随意来访的,

就是用 protected / private 把成员封装起来,开放一些共有的成员函数对成员合理的访问。

C语言没办法管理,易出错,全靠个人素质。所以,封装是一种更好、更严格的管理。

从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(中):https://developer.aliyun.com/article/1513642?spm=a2c6h.13148508.setting.17.5e0d4f0eApSShM

目录
相关文章
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
117 13
|
8天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
35 0
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
101 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
85 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
100 4
|
23天前
|
算法 编译器 C语言
【C语言】C++ 和 C 的优缺点是什么?
C 和 C++ 是两种强大的编程语言,各有其优缺点。C 语言以其高效性、底层控制和简洁性广泛应用于系统编程和嵌入式系统。C++ 在 C 语言的基础上引入了面向对象编程、模板编程和丰富的标准库,使其适合开发大型、复杂的软件系统。 在选择使用 C 还是 C++ 时,开发者需要根据项目的需求、语言的特性以及团队的技术栈来做出决策。无论是 C 语言还是 C++,了解其优缺点和适用场景能够帮助开发者在实际开发中做出更明智的选择,从而更好地应对挑战,实现项目目标。
46 0
|
2月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
32 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
32 4
|
23天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
46 10