当前位置: 首页 > news >正文

C++:类和对象(上)

一、引言

        类和对象是C语言所没有的,也是非常重要的一部分,理解它至关重要。

        C++中的类是面向对象编程的核心概念,它是一种用户自定义的数据类型,用于封装数据(属性)和操作数据的方法(函数)。类定义了对象的蓝图或模板,描述了对象应具备的特征和行为。对象则是类的具体实例,是根据类定义创建的实际实体,占用内存空间并包含具体的属性值。通过类和对象,C++实现了封装、继承和多态等面向对象特性,使代码更模块化、可重用和易于维护。

二、类的定义

2.1、类的定义格式

 class 为类的关键字,具体语法形式如下:

class name   //name为类的名字,自己取
{//……类的成员,可以为变量,可以为函数等};   //分号不能省略

(1)类的成员名称为了区分,要在前面加上 " _ " 或者 " m " 或者其他,具体看公司要求;

(2)struct 也可以充当关键字来定义类,与C语言不同的是,C++中的struct的成员可以为函数。但是,我们还是推荐使用 class 定义类;

(3)在类的内部定义的函数,都默认为内联函数;如果不想内联,可以在类中声明,然后在外部定义;

(4)类一般定义在头文件中。

2.2、类的访问限定符

        类的访问限定符:就是用来限定类的内部成员权限的符号。共有三个:private ,public ,protected 。

2.2.1、private (私有)

用法如下:

class student
{
private:int number;char[10] name;int age;int sex;
};

private 意味着私有,也就是只能在Student 内部访问,不能在外部访问。例如,下面的代码就是错误的,因为访问了私有成员。

Student student1;
std::cout << student1.name << endl;

对了,如果类的成员变量什么访问限制符都没有,则默认为被 private 修饰。

2.2.2、public (公有)

public 意思是公有,可以被外部访问。用法和private一样,如下:

class Student
{
private:int number;int age;char[10] name;
public:void DisplayAge(){cout << "学生年龄:" << age << endl;}
};

一般我们会把函数成员放在公有部分,私有部分一般都是存放一些数据的。

2.2.3、protected (保护)

用法和上面两个一样,这里不多说。

被 protected 修饰的成员为保护成员,在外部不能直接访问,只有通过派生类和内部才能访问。(派生类后面会将)

注意:这三个访问限定符没有先后之分,你可以先写公有部分,也可以先写私有部分

2.3、类域

(1)类域就是类定义的一个新的作用域,类的所有成员都在类的作用域中。在类的外部定义成员要用 :: ;

(2)类域影响编译器的查找规则。如果在调用一个函数时,没有指定类域,那么编译器就会默认该函数为全局函数,会在全局寻找其定义和声明,如果没有搜索到就会报错。

三、类的实例化

3.1、实例化的概念

创建一个类,只是创建了一个类,只是告诉编译器有这样的一个类,使用了吗?没有使用。你可以类比C语言中的结构体,你只是声明了一个结构体,并没有使用,使用结构体首先需要创建一个变量,类也是如此。

简单来说,类的实例化就是用类创建一个对象。一个类可以创建很多的对象。例如:

class Student
{
private:int _age;char[10] _name;
public:void SetInfo(int age, char* name){_age = age;_name = name;}
};int main()
{Student student1;   //实例化SetInfo( 18 , "zhangsan" );return 0;
}

3.2、对象的大小

用类创建一个对象叫实例化,系统就会给这个对象分配一个空间,那么这个空间有多大呢?里面的成员要不要遵循对齐原则?

3.2.1、对齐原则

        对象是存在内存对齐的,规则同C语言的结构体对齐,这里就省略了。

3.2.2、成员函数的储存

        对象中的内存里没有成员函数的位置。成员函数会单独放在一个地方等着调用。

3.2.3、空的类的对象的大小

求下面类创建的对象的大小:

class B
{};

最后结果是1,开辟1个字节的空间就是为了标记这个类的对象存在过。就像唯一能证明她来过的只有心里的那道疤痕

四、this 指针

先看以下代码,记住两个对象 s1 和 s2 。

class Student
{
private:int _age;int _number;char[10] _name;
public:void ShowData(){cout << "学生的学号:" << _number << endl;cout << "学生的姓名:" << _name << endl;cout << "学生的年龄:" << _age << endl;}//…………
};int main()
{Student s1;Student s2;//…………填写学生信息s1.ShowData();s2.ShowData();return 0;
}

好,我们可以看到,s1 和 s2 都分别调用了打印各自数据的函数。但是,函数是不会存储在对象中的,而是其他地方,所以,s1 和 s2 调用的同一个函数。问题来了,这个函数如何区分到底打印 s1 的数据还是 s2 的数据呢?答案就在 this 指针。

原来,编译器会在类的成员函数的第一个参数添加一个新的参数,叫 this指针。实际上,ShowData函数的原型为:

void ShowData( Student* const this)
{cout << "学生的学号:" << this->_number << endl;cout << "学生的姓名:" << this->_name << endl;cout << "学生的年龄:" << this->_age << endl;
}

通过 this 指针,函数就能区分到底该打印谁了。

注意:C++规定,不能在函数的实参和形参的位置写出 this 指针 ,但是可以在函数内部写出this指针。

练习:下面的代码是正常运行还是运行崩溃?

using namespace std;
class A
{
public:void Print()    {cout << "A::Print()" << endl;}
private:int _a;
};int main()
{A* p = nullptr;p->Print();return 0;
}

答案是正常运行。虽然 p 为空指针,还引用了成员函数Print(),但是,成员函数储存在别的地方,在编译的时候就已经确定了,所以不会报错。但是,如果成员函数涉及this指针的解引用,那就会报错了,因为空指针不能解引用。

五、C语言和C++实现栈的对比

C的代码就不放到这里了,大家都会。

下面是C++版本的栈:

//创建一个栈的C++版本
typedef int StackDataType;
class Stack
{
private:StackDataType* _arr;int _top;int _capacity;
public://栈的初始化void StackInit(){assert(this);_arr = nullptr;_top = _capacity = 0;}//入栈void StackPush(StackDataType x){if (_top == _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;StackDataType* tmd = (StackDataType*)realloc(_arr ,sizeof(StackDataType) * newCapacity);if (tmd == nullptr){perror("malloc fail!");exit(1);}_arr = tmd;_capacity = newCapacity;}_arr[_top++] = x;}//判断栈是否为空bool StackEmpty(){return _top == 0;}//出栈void StackPop(){assert(!StackEmpty());_top--;}//取栈顶StackDataType StackTop(){assert(!StackEmpty());return _arr[_top-1];}//销毁栈void StackDestory(){if (_arr)free(_arr);_arr = nullptr;_top = _capacity = 0;}//打印栈void StackPrint(){assert(!StackEmpty());for (int i = 0; i < _top; i++){cout << _arr[i] << " ";}cout << endl;}
};
int main()
{Stack s1;s1.StackInit();s1.StackPush(1);s1.StackPush(2);s1.StackPush(3);s1.StackPush(4);s1.StackPush(5);s1.StackPrint();s1.StackPop();s1.StackPrint();s1.StackDestory();return 0;
}

感觉C++更好用,把这些函数都封装了起来,而且使用起来更加方便。建议读者下去要亲自敲几个类体验体验!才能体会到类的魅力。

六、结语

        到这里,就已经认识,会用类了。但这还远远不够,更多细节还在后面,比如类里面自带的几个函数等。加油,追梦人!

http://www.dtcms.com/a/365527.html

相关文章:

  • 集成运算放大器的作用、选型和测量指南-超简单解读
  • 夸克网盘辅助工具 QuarkPanTool 分析
  • 代码随想录算法训练营第一天 || (双指针)27.移除元素 26.删除有序数组中的重复项 283.移动零 977.有序数组的平方
  • 从 “能说会道” 到 “能做会干”:AI Agent 技术突破,如何让人工智能拥有 “行动力”?
  • Linux 创建服务 使用systemctl 管理
  • uni app 的app端 写入运行日志到指定文件夹。
  • 腾讯云《意愿核身移动 H5》 快速完成身份验证接入
  • 国产CAD皇冠CAD(CrownCAD)建模教程:汽车驱动桥
  • HTML5 标题标签、段落、换行和水平线
  • shell-awk命令详解(理论+实战)
  • 【面试场景题】1GB 大小HashMap在put时遇到扩容的过程
  • 第七章 表达:成果展示--创建第二大脑读书笔记
  • 10名机械画图人员如何共享一台云服务器的软硬件资源进行设计办公
  • ArcGIS解决csv或者excel转换为矢量的坐标问题
  • 第二章 Windows 核心概念通俗解析
  • 03 - HTML常用标签
  • 【学Python自动化】 9.1 Python 与 Rust 类机制对比学习笔记
  • PyTorch 和 Transformer的区别与联系
  • Linux 入门到精通,真的不用背命令!零基础小白靠「场景化学习法」,3 个月拿下运维 offer,第二十五天
  • 农业XR数字融合工作站,赋能农业专业实践学习
  • Qt为什么要引入QML语言?
  • 八、算法设计与分析
  • 瑞芯微rv1126 linux内核使用spidev驱动
  • 【Unity基础】两个关于UGUI中Text对非英文字体支持的问题
  • 在线性代数里聊聊word embedding
  • 在Excel和WPS表格中隔多行插入一个空白行
  • 【Linux 内存管理】2 进程地址空间 - vm_area_struct 数据结构
  • 【Zotero】插入中文参考文献遇到的问题
  • 【数据处理工具】依据图层批量分割要素,并自动处理碎图斑
  • Zynq中级开发七项必修课-第七课:AXI DMA (PL→PS) 数据上传