C++之类与对象(上)

news2024/11/28 22:37:53

目录

一、类的定义

二.类的访问限定及封装

1.访问限定

2.封装

三.类的作用域和实例化

2.类的实例化

四.类的对象大小的计算

1.类成员存储方式

2.结构体内存对齐规则

五.类成员函数的this指针

1.this指针的引出

2.this指针的特性

3.C语言和C++实现Stack的对比


一、类的定义

class className
{
// 类体:由成员函数和成员变量组成
};   // 一定要注意后面的分号
 class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面 号不能省略。 类体中内容称为类的成员:类中的变量称为类的属性成员变量; 类中的函数称为类的方法或者 成员函数
        类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成
联函数处理。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
一般情况下,更期望采用第二种方式。
        成员变量命名规则的建议:
class Date
{
public:
 void Init(int year)
 {
 _year = year;
 }
private:
 int _year;
};

成员变量和函数形参不要一样,一般都是加个前缀或者后缀标识区分就行。

二.类的访问限定及封装

1.访问限定

  C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

 

访问限定符说明:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
问题:struct和class有什么区别呢?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来
定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类
默认访问权限是private。注意:在继承和模板参数列表位置,struct和class也有区别。

2.封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用
户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日
常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
class Stack
{
public:
	// 成员函数
	void Init(int capacity = 4);
	void Push(int x);

private:
	// 成员变量
	int* a;
	int size;
	int capacity;
};

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

三.类的作用域和实例化

1.类定义了一个新的作用域,类的所有成员都在类的作用域中类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
class Person
{
public:
 void PrintPerson();
private:
 char _name[20];
 char _gender[3];
 int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPerson()
{
 cout << _name << " "<< _gender << " " << _age << endl;

2.类的实例化

用类类型创建对象的过程,称为类的实例化

1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没
有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个
类,来描述具体学生信息。
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量。
类本身不占内存空间,类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设 计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象
才能实际存储数据,占用物理空间。

 

四.类的对象大小的计算

1.类成员存储方式

类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算
一个类的大小,我们先通过对下面的不同对象分别获取大小来分析看下
// 类中既有成员变量,又有成员函数
class A1 {
public :
    void f1 (){}
private :
    int _a ;
};
// 类中仅有成员函数
class A2 {
public :
  void f2 () {}
};
// 类中什么都没有 --- 空类
class A3
{};
通过sizeof()可以得到结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐 。注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象
问题:成员函数为什么没有在类里面呢?
我们先看看下面这种存储方式是否合理
每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一 个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。所以对象只保存成员变量,成员函数存放在公共的代码段

2.结构体内存对齐规则

1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

五.类成员函数的this指针

1.this指针的引出

我们先定义一个Date类

class Date
{ 
public:
 void Init(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year;     // 年
 int _month;    // 月
 int _day;      // 日
};
int main()
{
 Date d1, d2;
 d1.Init(2022,1,11);
 d2.Init(2022, 1, 12);
 d1.Print();
 d2.Print();
 return 0;
}
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函
数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成
所以说,实际的调用情况是这样的,不过这是编译器干的事,我们不要去在实参和形参显示表示,但是在函数里面我们可以使用this指针。
void Init(Date*this,int year, int month, int day)
 {
  this->_year = year;
  this->_month = month;
  this->day = day;
 }

 d1.Init(&d1,2022,1,11);
 d2.Init(&d2,2022, 1, 12);

2.this指针的特性

1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值
2. 只能在“成员函数”的内部使用。
3. this指针本质上是“成员函数”的形参,一般是存储在栈上,也有可能通过ecx寄存器自动传递(算是一种优化)的,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递。
问题:this指针可以为空吗?
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
 void Print()
 {
 cout << "Print()" << endl;
 }
private:
 int _a;
};
int main()
{
 A* p = nullptr;
 p->Print();
 return 0;
}

我们可以发现p是一个空指针,好像会发生空指针的解引用,但事实上该程序正常运行。因为

Print()函数没有在对象A里面所以这里p->Print()不会发生解引用,程序自然就会正常运行。

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
};

在看这一个程序,在Print()函数里面,有隐含的解引用this->_a,所以程序会发生崩溃。

通过以上两个程序,说明this指针可以为空,只要不要解引用空指针就行,也说明不要一看到->或者*就感觉一定会发生解引用。

3.C语言和C++实现Stack的对比

1.C语言实现

typedef int DataType;
typedef struct Stack
{
 DataType* array;
 int capacity;
 int size;
}Stack;
void StackInit(Stack* ps)
{
 assert(ps);
 ps->array = (DataType*)malloc(sizeof(DataType) * 3);
 if (NULL == ps->array)
 {
 assert(0);
 return;
 }
ps->capacity = 3;
 ps->size = 0;
}
void StackDestroy(Stack* ps)
{
 assert(ps);
 if (ps->array)
 {
 free(ps->array);
 ps->array = NULL;
 ps->capacity = 0;
 ps->size = 0;
 }
}
void CheckCapacity(Stack* ps)
{
 if (ps->size == ps->capacity)
 {
 int newcapacity = ps->capacity * 2;
 DataType* temp = (DataType*)realloc(ps->array, 
newcapacity*sizeof(DataType));
 if (temp == NULL)
 {
 perror("realloc申请空间失败!!!");
 return;
 }
 ps->array = temp;
 ps->capacity = newcapacity;
 }
}
void StackPush(Stack* ps, DataType data)
{
 assert(ps);
 CheckCapacity(ps);
 ps->array[ps->size] = data;
 ps->size++;
}
int StackEmpty(Stack* ps)
{
 assert(ps);
 return 0 == ps->size;
}
void StackPop(Stack* ps)
{
 if (StackEmpty(ps))
 return;
 ps->size--;
}
DataType StackTop(Stack* ps)
{
 assert(!StackEmpty(ps));
 return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
 assert(ps);
 return ps->size;
}
int main()
{
 Stack s;
 StackInit(&s);
 StackPush(&s, 1);
 StackPush(&s, 2);
 StackPush(&s, 3);
 StackPush(&s, 4);
 printf("%d\n", StackTop(&s));
 printf("%d\n", StackSize(&s));
 StackPop(&s);
 StackPop(&s);
 printf("%d\n", StackTop(&s));
 printf("%d\n", StackSize(&s));
 StackDestroy(&s);
 return 0;
}
可以看到,在用C语言实现时,Stack相关操作函数有以下共性:
1.每个函数的第一个参数都是Stack*;
2.函数中必须要对第一个参数检测,因为该参数可能会为NULL;
3.函数中都是通过Stack*参数操作栈的;
4.调用时必须传递Stack结构体变量的地址;
5.结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据
的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。

2.C++实现方法

typedef int DataType;
class Stack
{
public:
 void Init()
 {
 _array = (DataType*)malloc(sizeof(DataType) * 3);
 if (NULL == _array)
 {
 perror("malloc申请空间失败!!!");
 return;
 }
 _capacity = 3;
 _size = 0;
 }

void Push(DataType data)
 {
 CheckCapacity();
 _array[_size] = data;
 _size++;
 }

 void Pop()
 {
 if (Empty())
 return;
 _size--;
 }

 DataType Top(){ return _array[_size - 1];}
 int Empty() { return 0 == _size;}
 int Size(){ return _size;}

void Destroy()
 {
  if (_array)
   {
   free(_array);
   _array = NULL;
   _capacity = 0;
   _size = 0;
   }
 }


private:
 void CheckCapacity()
 {
   if (_size == _capacity)
   {
     int newcapacity = _capacity * 2;
     DataType* temp = (DataType*)realloc(_array, newcapacity *
     sizeof(DataType));
      if (temp == NULL)
      {
      perror("realloc申请空间失败!!!");
      return;
      }
   _array = temp;
   _capacity = newcapacity;
   }
 }


private:
 DataType* _array;
 int _capacity;
 int _size;
};


int main()
{
 Stack s;
 s.Init();

 s.Push(1);
 s.Push(2);
 s.Push(3);
 s.Push(4);
 
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Pop();
 s.Pop();
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Destroy();
 return 0;
}
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在
类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,即C++中 Stack *
参数是编译器维护的,C语言中需用用户自己维护

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/367598.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux的kdump分析

文章目录一 系统环境二 下载和安装kerner-debuginfo三 启动crash四 crash常用命令一 系统环境 进行kdump分析的主机是CentOS-7.9系统。 [rootcanway ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootcanway ~]# uname -r 3.10.0-1160.el7.x86_64检查…

智慧新零售网络解决方案,助力新零售企业数智化转型

随着数字化时代的不断发展&#xff0c;新零售连锁业务模式“线上线下”融合发展&#xff0c;数据、设备在逐渐增加&#xff0c;门店数量也会随着企业规模的扩大而增加&#xff0c;但由于传统网络架构不稳定、延时、容量小影响服务质量&#xff08;QoS&#xff09;、分支设备数量…

【java】Spring Cloud --Spring Cloud Alibaba 教程

文章目录Spring Cloud Alibaba是什么Spring Cloud AlibabaSpring Cloud Alibaba 组件Spring Cloud Alibaba 的应用场景Spring Cloud 两代实现组件对比Spring Cloud Alibaba 版本依赖Spring Cloud Alibaba 组件版本关系Spring Cloud Alibaba NacosNacos 的特性服务发现服务健康监…

写博客有哪些好用的工具和软件?

写博客有哪些好用的工具和软件&#xff1f; 在各种平台上写文章&#xff0c;要有一个好用的编辑器&#xff0c;使用简单方便、不用花很多时间排版、能预览效果等。此外&#xff0c;文章还需要配图片、动图和视频等&#xff0c;所以需要好用的截图软件、视频录制和剪辑软件。 …

ME1M 报表Layout调整

众所周知&#xff0c;SAP的许多报表都可以选择以ALV的格式输出&#xff0c;ALV格式界面友好&#xff0c;业务人员可以按照自己的需求去调整报表输出格式&#xff0c;同时也方便导出到本地文件。大多数报表的选择界面上都有一个字段scope of list, 将其改成ALV&#xff1a; 偏偏…

该学会是自己找bug了(vs调试技巧)

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍c语言初阶的最后一篇.有关调试的重要性. 金句分享…

Linux基本介绍与常用操作指令

参考链接&#xff1a; Linux面试必备20个常用命令_无 羡ღ的博客-CSDN博客_linux常用命令 1. Linux简介 Linux是一个支持多用户、多任务、多线程和多CPU的操作系统&#xff0c;特点是免费、稳定、高效&#xff0c; 一般运行在大型服务器上。 1.1 常用目录简介 /&#xff1a;根目…

【storybook】你需要一款能在独立环境下开发组件并生成可视化控件文档的框架吗?(一)

storybook介绍入门说说用法prop-types.stories.jsx介绍 你肯定用过ant-design或者elemenUI中的一款UI组件库&#xff0c; 你肯定打开过它们的官网&#xff0c; 你是否好奇过&#xff0c;页面上展示的案例&#xff0c; 你为啥可以操作&#xff0c;还能查看源码供你CV?它们是…

Docker之路(6.docker容器数据卷、具名挂载匿名挂载、权限挂载)

1. 什么是容器数据卷 如果数据都在容器中&#xff0c;那么容器删除后&#xff0c;数据就会丢失&#xff01; 所以希望容器的数据能够存到本地 容器之间可以又一个数据共享的技术&#xff01; Docker容器中产生的数据&#xff0c;可以同步到本地&#xff0c;这就是卷技术。 就是…

Nacos Ignore the empty nacos configuration and get it based on dataId

1.配置错误 dataId问题 启动日志&#xff1a; 使用properties格式的文件&#xff1a; Ignore the empty nacos configuration and get it based on dataId[xxx-server] & group[DEFAULT_GROUP] Ignore the empty nacos configuration and get it based on dataId[xxx-s…

【Redis中bigkey你了解吗?bigkey的危害?】

一.Redis中bigkey你了解吗&#xff1f;bigkey的危害&#xff1f; 如果面试官问到了这个问题&#xff0c;不必惊慌&#xff0c;接下来我们从什么是bigkey&#xff1f;bigkey划分的类型&#xff1f;bigkey危害之处&#xff1f; 二.什么是bigkey&#xff1f;会有什么影响&#xff…

九头蛇hydra爆破http示例

使用hydra执行http表单暴力破解 通过浏览器自带分析得知: 提交地址:http://10.0.0.115/student_attendance/ajax.php?action=login 提交方式:POST 提交数据:username=a&password=a 服务响应:3 根据以上收集的信息就可以使用hydra进行密码爆破 hydra 10.0.0.115 http…

【第一章:Spring概述、特点、IOC容器、IOC操作bean管理(基于xml方式创建对象,注入属性)】

第一章&#xff1a;Spring概述、特点、IOC容器、IOC操作bean管理&#xff08;基于xml方式创建对象&#xff0c;注入属性&#xff09; 1.Spring是什么&#xff1f; ①Spring是一款主流的java EE 轻量级开源框架。 ②广义的Spring&#xff1a;Spring技术栈&#xff0c;Spring不再…

华为OD机试题,用 Java 解【计算最大乘积】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

【基础算法】哈希表(开放寻址法)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

聚观早报|饿了么星选停止运营;百度2022财报全年营收1236.75亿元

今日要闻&#xff1a;饿了么星选停止运营&#xff1b;百度2022财报全年营收1236.75亿元&#xff1b;微软收购暴雪获力挺&#xff1b;全国首例&#xff01;这一代练平台被叫停&#xff1b;现代汽车已开始在美国生产电动汽车 饿了么星选停止运营 2 月 22 日&#xff0c;饿了么旗下…

使用Vite创建VUE项目

使用Vite来创建vue项目 很久没有接触前端的技术了&#xff0c;一下子就冒出来很多技术&#xff0c;有的根本没见过呀&#xff0c;vue变成了3&#xff0c;vue-cli也变得不好用了&#xff0c;说vite很好用&#xff0c;那就创建个项目试试吧。 有一点前提哈&#xff0c;电脑上要…

华为OD机试题,用 Java 解【磁盘容量排序】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

【100个 Unity实用技能】 | 将Dictionary字典中的数据序列化 到Unity Inspector监视器面板

Unity 小科普 老规矩&#xff0c;先介绍一下 Unity 的科普小知识&#xff1a; Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案&#xff…

【vue2每日小知识】实现store中modules模块的封装与自动导入

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;省去我们store仓库中分模块时的需要每次导入index的问题 目录 【前言】在store中如何简…