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

C++ - 类和对象 # 类的定义 #访问限定符 #类域 #实例化 #this 指针 #C++ 与 C语言的比较

目录

前言

一、类的定义

1、类定义格式

1)、为区分成员变量,一般会在其前或者后面添加 _ or m

2)、C++ 中的struct 也可以用来定义类

2、访问限定符

3、类域

二、实例化

1、实例化概念

2、对象大小

三、this 指针

四、关于考点

例1:

例2:

例3:

五、C++ 和 C语言实现Stack 对比

总结


前言

路漫漫其修远兮,吾将上下而求索;


一、类的定义

1、类定义格式

  • class 为定义类的关键字,Stack 为类的名字,{} 中为类的主体,注意类定义结束的时候其后面的分号不可以省略。类体中的内容称为类的成员:类中的变量称为类的属性或成员变量,类中的函数称为类的方法或者成员函数
  • 为了区分成员变量,一般习惯上成员变量会加一个特殊的标识,如成员变量前或者后面加 _ 或者 m 开头,注意C++ 中这个并不是强制的,只是一些惯例,具体需要看公司的要求;
  • C++ 中 struct 也可以定义类,C++兼容C中的struct 的用法,同时struct 升级成了类,明显的变化是struct 中可以定义函数,一般情况下还是推荐使用class 定义类。
  • 定义在类里面的成员函数默认为inline 内联函数

类和结构体很像,只不过在结构体中不能放函数,结构体本质上是变量的复合体;类型分为内置类型与自定义类型;C/C++ 中提供的内置类型,标识数据大小的整形、浮点数、指针、字符、字符串等,这些类型可以组合构成现实生活中绝大多数类型;

注:字符串几乎可以表示一切类型,整形和浮点数等可以以字符的形式存在字符串中,只不过为了方便计算,所以整形、浮点数等单独存放并且有属于自己的一套规则存储在内存中;

自定义类型也就是类;

C语言的结构体,数据和方法都是分离的,函数命名时需要加前缀、使用相关的函数需要传参……用起来比较麻烦;C++ 中的类,数据和方法(成员变量与成员函数)均封装在一起,这样就解决了很多函数命名要加前缀的问题,同时还通过访问限定符限制类中哪些可以访问,哪些不可以访问(只能通过调用类中公有的函数来操纵数据--> 使用更加规范),还可以通过this 指针(编译器隐式传参)解决传参问题

1)、为区分成员变量,一般会在其前或者后面添加 _ or m

修改如下:

如果成员变量不添加标识,有时候函数参数的名字可能会与成员变量相撞,不好区分;故而在C++ 类中,一般情况下会在成员变量前添加 _ 或者m ;

注:标准并没有定义此做法

2)、C++ 中的struct 也可以用来定义类

Q1:calss 定义的类与用struct 定义的类,有什么区别?

  • C++ 中的struct 兼容C语言中struct 的使用;class 与 struct 均可以定义成员变量、成员函数,二者的区别在于访问限定符

什么是访问限定符?

2、访问限定符

  • C++一种实现封装的方式,用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性地将其接口提供给外部的用户使用
  • public 修饰的成员在类外可以直接被访问;protected private 修饰的成员在类外不能直接被访问,此处暂时将看作protected 和 private 是一样的(后面继承章节再详谈)
  • 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域到 } 即类结束;
  • class 定义成员没有被访问限定符修饰时默认为private ,struct 默认为public;
  • 一般成员变量都会被限制为 private/protected ,需要给别人使用的成员函数会放为public;

Q1:访问限定符是什么?

  • 访问限定符是C++ 实现封装的一种方式,即C++的类的属性和方法(类的成员变量与成员函数)放在一起;然后通过访问限定符来控制成员变量、成员函数的权限,选择性地给外面进行使用;

Q2:上面所提的三种访问限定符的含义?

一个类中可以有多个访问限定符;

使用例子如下:

#include<assert.h>
#include<iostream>
using namespace std;class stack
{
public://公有成员函数void Init(int n = 4){//开辟空间_array = (int*)malloc(sizeof(int) * n);//判空if (_array == nullptr){perror("malloc");return;}//处理变量_top = 0;_capacity = n;}void Push(int x){//扩容//..._array[_top++] = x;}int Top(){assert(_top);return _array[_top - 1];}void Destroy(){free(_array);_array = nullptr;_top = _capacity = 0;}
private://私有成员变量int* _array;size_t _top;size_t _capacity;
};

Q3:struct 与 class 的区别

对于calss ,在calss 中定义的成员未被访问限定符修饰的时候,默认为private

将上述例子中的成员函数加上访问限定符public,如下:

而对于struct , 在struct 之中定义的成员未被访问限定符修饰,默认为public

当前,实践当中,绝大部分的地方均是使用calss 来定义类

一般成员变量会被限制为private / protected ,需要给别人使用的成员函数会被放为 public,使用代码如下:

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

3、类域

  • 类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用 :: 作为用域操作符指明成员属于哪个类域
  • 类域影响的是编译的查找规则,例如:在实现栈的时候,Init 如果不指定类域Stack ,那么编译器就会把Init 当作全局函数,那么编译时,找不到array 等成员的声明/定义在哪里就会报错;指定类域Stack, 就是知道Init 是成员函数,当前域找不到的array 等成员,就会到类域中进行查找;

C++ 中有四个域:局部域、全局域、命名空间域、类域;其中,类定义的域就叫做类域,类的所有成员均是在类的作用域中的,在类外定义成员时,需要用 :: (访问限定符) 指明成员属于哪个类域;

作用域不同,可以存在同名的函数,并不会冲突,也不需要去符合重载的规则,如下图;

意味着,在不同的类中可以定义同名的函数、变量,隔离了类与类之间的命名冲突,即类形成了一个新的域;

Q1:默认在类中定义的函数为内联函数,有没有什么方法让在类中的成员函数不是内联函数?

  • 在类中的函数也可以将其声明域定义分离 , 在项目中非常支持这么做;

类的声明与定义应该怎么写?

在 .h 文件中实现类,在类中进行函数的声明,然后在 .cpp 文件中实现其定义并且需要指定类,如下:

其中需要注意的是,带有缺省参数的函数,在声明的时候带上了缺省参数,那么在定义的时候就不能带上;还有就是类中成员函数的声明与定义分离的时候,在定义这个函数时,需要指明类域;

Q2:为什么定义的时候需要指定类域?

  • 单从Stack.cpp 上看,函数Init 就是一个普通的成员函数,对于普通函数要使用一些变量的话,这些变量要么时你在当前局部域中定义的,要么是在参数(形参)定义的,要么是在全局定义的;即会在局部域、全局域中去查找;编译器的默认首先会在全局去查找,当在全局找不到的时候就回去全局中查找,倘若在全局中找不到就会报错;显然,Init 这个函数要使用的数据是在类域中,不在全局域中也不在局部域中,如果不指定类域改变编译器的查找规则(指定类域就是直接告诉编译器到类域中去找),就会找不到而报错;

二、实例化

1、实例化概念

用类类型在物理内存中创建对象的过程,称为类实例化出对象

类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时才会分配空间;

一个类可以实例化出多个对象,实例化出的对象会占用实际的物理空间,存储成员变量。

还可以这么理解:类实例化出的对象就像现实当中使用建筑设计图造出来的房子,类就像是设计图,设计图规划了有多少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才可以住人。同样类就如同设计图纸一样,不能存储数据,实例化出的对象分配物理内存存储数据;

注:此处所述的“物理空间”也是相对的;内存分为物理内存、虚拟内存;

Q1:类存储在什么地方、对象存储在什么地方?

  • 类和对象存储的地方是不同的;平时我们写的代码是存放在文件当中。代码写好之后就会通过编译器去进行编译(预处理->编译->汇编)、链接,最后生成一个可执行程序(可执行程序就是指令),即类被编译之后,其中的方法、变量以及对象实例化等过程会生成一系列指令

注:程序运行起来之后会依靠一个叫进程的东西,进程便会将这些指令加载到其代码之中,CPU会让进程运行也就是让该可执行程序加载到内存之中,然后依次执行该代码;

Q2:对象(变量)的空间又是如何开辟出来的?

  • 编译器在编译的时候便计算好了这些变量会占用多少字节的空间,函数调用会创建栈帧(例如esp、ebp 维护的空间),局部变量在栈之中;

2、对象大小

Q1:类对象有哪些成员?

  • 类实例化出来的每一个对象,都有自己独立的数据空间,所以对象中肯定包含了成员变量,是否会包含成员函数?首先函数被编译后是一段指令,对象当中没办法存储,这些指令存储在一个单独的区域(代码段),如果在对象中非要存储成员函数的话就只有存储成员函数的指针;

Q2:对象当中是否有必要存储成员函数的指针?

  • 就拿上面写过的类Date 为例,Date 实例化d1 和 d2 两个对象,d1 和d2 都有各自独立的成员变量Init/Print ,显然函数指针都是一样的,如果在每一个Date 实例化出的对象中均存储函数指针,倘若实例化出来的对象很多,显然这会浪费许多的空间;可见,函数指针并不需要存储在对象之中的,只要保证我们可以利用对象使用到其类中的成员函数就可以了;函数指针是一个地址,调用函数被编译成汇编指令(call 地址),其实编译器在编译链接时,就要找到函数的地址,并不是在运行的时候进行查找,只有动态多态是在运行中查找,此时才需要存储函数的地址;

在x86环境,代码运行结果如下:

 

分析:

从上例之中可以得知,显然,在类对象之中并未存放函数指针;即类之中只会存放成员变量,计算一个类的大小的时候,只按照对齐规则计算成员变量所占的内存大小即可;   

Q3:成员函数是如何被包含的呢?

  • 函数被编译后是一段指令,对象中是不存储的,这些指令会存储在一个单独的区域(代码段)中,如果对象非要存储该函数的话,也只会存储该函数的地址(在编译时确认);编译时,若有该函数的定义,那么该函数便会被编译成一串指令放到一个叫“公共代码段”的地方,找到其第一条指令的地址便是函数的地址(在VS之中实际上是jump指令的地址);函数的地址并不需要进行存储,因为在调用函数的时会在编译、链接阶段被处理变成call 的地址;

注:内联函数没有地址,直接会被编译成汇编指令

  • 1、如果在当前语句直接就有该函数的定义,调用该函数的语句被编译成一串指令之后便会有该函数的地址(call 指令);

  • 2、如果只有声明,编译时会过,晚一点会链接时在符号表中找到该函数的地址;

总的来说,在类中其实是可以存放函数的地址,但是没有必要,因为只要在调用函数时声明了其类,那么编译器也是可以找到该函数的;

上例中st1 、st2 和 st3 中的成员变量各自有各自的一份,但他们的成员函数是同一个;

C++中规定类实例化对象也要符合内存对齐的规则;

从上图中,A类实例化出来的对象按照对齐规则计算出来的所占内存大小为8 byte ,这是可以理解的,但是为什么没有成员变量的类B与没有成员变量成员函数的类C 实例化出来的对象还要占1byte?

  • 成员函数不占空间(成员函数的地址并不会存放到对象之中),类B 、C实例化出来的对象占了1byte 这是因为系统设置了一种机制:无成员变量的类均可以叫做空类,会为空类开辟1byte 的空间但是什么也不会存储;

Q4:为空类开辟的1byte 的空间是做什么的?

为了占位,表示这个对象存在过;如果一个byte 都不给的话,又怎么表示这个对象被定义出来了?并且如果空类实例化出来的对象没有空间的话,想要取地址该对象又该如何?本质上只要给这个空类一块空间就行了,可以是2byte、3byte……但是显然给1byte 更节省空间,所以规定空类占了1byte 的空间;

小复习-内存对齐的规则

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

三、this 指针

Date 类中有Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,当d1 调用Init 和Print 函数时,该函数是如何知道应该访问的是d1 对象还是d2 对象的呢?那么这里就要看到C++ 给的一个隐含的this 指针来解决这里的问题;

编译器编译后,类的成员函数默认会在形参的第一个位置,增加一个当前类类型的指针,叫做this 指针;比如Date类的Init 的真实原型为,void Init(Date* const this , int year , int month , int day)

类的成员函数中访问成员变量,本质上都是通过this 指针进行访问的,如Init 函数中给 _year 赋值,this->_year = year;

编译器编译后,类的成员函数默认会在形参的第一个位置,增加一个当前类类型的指针,叫做this 指针

例子代码如下:

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;Date d2;d1.Init(2025, 4, 25);d2.Init(2004, 11, 10);d1.Print();d2.Print();return 0;
}

注:编译中会有一个语法检查的过程,从语法设计的角度来说你用任意一个函数,是需要找到其出处,而出处可以是声明也可以是定义;此处的两个函数(初始化、打印)是需要针对实际的物理空间进行的;

  • C++编译器会将this 指针叫做隐含的this 指针

隐含:编译器会在成员函数的实参和形参的位置加一个叫做this 指针的参数;

再回过头看上面的例子,上述的成员函数Init 看着有3个参数,实际上有4个参数:void Init(Date* const this , int year , int month , int day); 成员函数Print 看似没有参数,实际上有一个参数:void Print(Date* const this);

this 指针可以显式地使用,如下:

(谁调用,this 指针便指向谁)

成员函数在访问成员变量的时候并非访问声明中的成员变量,声明中的成员变量的存在,可以让函数在编译时找到该成员变量的出处,以保证编译器在编译时不会报错,实际上函数调用是通过this 指针做到那个对象调用便访问哪个对象

Q2:this 指针的特点

  • this 指针会在成员函数的第一个形参的位置上,在实参的位置上编译器也会加,但是我们并不能在实参的位置处显式地传参,也不能在形参的位置上显式地写出来;

四、关于考点

  • 1、对象之中并不会存储成员函数的指针
  • 2、隐含地传递this 指针(如果是对象调用就传递这个对象的地址过去,如果是对象指针,便就直接传指针过去)

例1:

答案为:C

解析:需要注意的是编译报错是指语法错误,而空指针、野指针是运行时的逻辑错误;

p->Print(); 从编译过后的角度,这个地方会变成call  + 函数地址;此函数在编译的时候是一段指令,存储在代码段之中,其地址会放在符号表之中,在编译时找到该函数的地址便确定了其地址;而p->Print(); 并非进行了解引用的操作,此处是A类对象p在调用其成员函数Print();

Q1:指针变量p 充当的作用是什么?

  • 1、编译器调用此成员函数的时候,编译器得知道这个成员函数是从哪里调用得,如果写作Print(); 那么编译器就只知道去全局中查找,显然是找不到的;此处使用对象或者对象得指针,就会告诉编译器这个函数在哪一个类域之中,编译器就好找到其出处(声明或者定义)
  • 2、调用成员函数会传递this 指针。故而此处相当于将对象指针传递给了this 指针,p 为nullptr ,所以Print();中的this 指针也为nullptr ,但在函数Print() 中并没有使用this 指针;所以此处正常运行;

例2:

答案为:B

解析:因为成员变量存放在其对应的对象之中,而此处的this 指针为nullptr, 想要获得_a 就得对this 进行解引用操作,而对空指针解引用为运行错误;即Print(); 中的 cout<<_a<<endl; 的完整写法为: cout<< this->_a<<endl;

例3:

答案:A

解析:之前计算对象大小的时候并未计算this 指针,单单凭这一点,我们可以确定this 指针不存储在对象之中,排除E.对象里面;而堆上的空间是需要动态开辟的,显然this 指针并不是动态开辟的空间,排除B.堆;this 指针是该类的const 指针,this 指针的指向不可修改;而const 修饰的变量并非就是放在常量区,const 修饰的变量具有常属性的变量,即语法上不可修改,但是严格来说也是可以修改的,通对其指针解引用也是可以达到修改的目的(间接修改),排除D.常量区;

this 指针也不可能存放在静态区,因为静态区中存放的变量是静态或者全局的,而this 指针的生命周期并非是全局的,排除C.静态区;

经过排除法,this 指针应该是存储在栈中的;this 指针是在不断变化的,本质就是一个形参,而参数是存储在栈上的(this 指针本质上就是一个成员函数的形参,哪个对象调用这个成员函数,就将哪个对象的地址给给this 指针)

在有些编译器中会将this 指针存在寄存器中,vs 下通过ecx 这个寄存器进行传参(并非是压栈)

Q2:为什么是通过寄存器ecx 来传参?

  • 因为在成员函数之中,this 指针会被频繁地访问,况且在存储体系之中,寄存器的读取速度是最快的

正是因为编译器在传this 指针的方式均不相同,故而此处统一默认this 指针存储在栈上;

vs 下代码转到反汇编:

注:在vs 中x64 环境下使用的寄存器为rcx , x86 环境下使用的寄存器为ecx;

五、C++ 和 C语言实现Stack 对比

面向对象的三大特性:封装、继承、多态,下面的对比我们可以初步了解一下封装;

注:面向对象有很多特性,eg. 抽象、反射……只不过这三种特性是最出名的;

通过下面两份代码的对比,我们发现C++ 实现Stack 形态上还是发生了挺多的变化,底层和逻辑上没有什么变化;

  • C++ 中数据和函数都放在了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++ 封装的一种体现,相较于C语言,这个变化十分重要;这里的封装本质是一种更加严格规范的管理,避免出现乱访问修改的问题。当然封装不仅仅是这样,本文只涉及了点皮毛,想要深入了解,可以看博主后面的相关的博文;
  • C++ 中有一些相对方便的语法,比如Init 给的缺省参数会方便很多,成员函数每次不需要传对象的地址,因为this 指针隐含传递了,这样的话,在传参层面使用起来就方便了很多;并且使用类型并不用像C语言中需要typedef ,在C++ 中可以直接用类名当作类型来使用
  • 在本文中实现的Stack 与 C语言版本比起来,变化了很多,实际上本质变化不大;后面学习到了用适配器来实现Stack , 就可以真正感受C++ 的魅力了;

C语言是面向过程,即关注于过程实现的语言;而C++ 是面向对象,即更加关注对象和对象、类和类之间的关系;

  • 抽象理解:假设在学校之中搞了一个局部的外卖网;如果用面向过程维度去关注,关注的是商家如何接单、同学如何下单、帮跑腿的同学如何接单……即关注的是整个过程,而倘若用面向对象去设计该系统,则会关注这个系统中有哪些角色(可以把一个角色设计为一个类)比如说:学生、食堂的阿姨、老板、接单兼职的同学……不考虑细节,面向对象设计的是有一个同学下单便可以用改同学去实例化出一个对象,有一个食堂阿姨接单了,便用阿姨这个类去实例化出一个对象,有一个兼职的同学接跑腿的单便可以用兼职同学的类去实例化出一个对象……相当于在这个系统中,看到的是这三个类型的交互,它们之间的关系……其次在看此系统的时候可以想象在这个虚拟世界中有很多个同学、阿姨、兼职的同学……

注:封装是更加严格的管理;类这部分的设计就体现出了一部分的封装;封装就是把这些东西包起来(类),并且进行一些限制(访问限定符),图解如下:

C语言版本Stack 的实现,代码如下:

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int STDateType;
typedef struct Stack
{STDateType* a;int top;int capacity;
}ST;void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->top = ps->capacity = 0;
}void STDestroy(ST* ps)
{assert(ps);//释放、置空置零free(ps->a);ps->a = NULL;ps->top= ps->capacity = 0;
}//push
void STPush(ST* ps, STDateType x)
{assert(ps);//确保空间足够 -> 判断扩容if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDateType* tmp = (STDateType*)realloc(ps->a, sizeof(STDateType) * newcapacity);if (tmp == NULL){perror("ranlloc fail");return;}//细节处理ps->a = tmp;ps->capacity = newcapacity;}//插入数据ps->a[ps->top] = x;ps->top++;
}//判空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//删除栈顶的数据
void STPop(ST* ps)
{assert(ps);//确保有数据可以删除assert(!STEmpty(ps));ps->top--;
}//获取栈顶的数据
STDateType STTop(ST* ps)
{assert(ps);return ps->a[ps->top - 1];
}//获取数据个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}//使用
//int main()
//{
//	ST s;
//	STInit(&s);
//
//	STPush(&s, 1);
//	STPush(&s, 2);
//	STPush(&s, 3);
//	STPush(&s, 4);
//	STPush(&s, 5);
//
//	while(!STEmpty(&s))
//	{
//		printf("%d\n", STTop(&s));
//		STPop(&s);
//	}
//
//	STDestroy(&s);
//	return 0;
//}

从C语言版本中可以明显地知道,我们实现的栈Stack的数据和方法是分离的,在写栈的接口函数的时候需要加上ST以作为区分,如果不加上就难以区分;

C++版本Stack 实现,代码如下:

#include<assert.h>
#include<iostream>
using namespace std;typedef int STDateType;
class Stack
{
public://成员函数//初始化void Init(int n = 4){_a = (STDateType*)malloc(sizeof(STDateType) * n);if (_a == nullptr){perror("malloc fail");return;}_capacity = n;_top = 0;}//pushvoid Push(STDateType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDateType* tmp = (STDateType*)realloc(_a, sizeof(STDateType) * newcapacity);if (tmp == nullptr){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}//popvoid Pop(){assert(_top);_top--;}//判空bool Empty(){return _top == 0;}//获取栈顶的数据STDateType Top(){assert(_top);return _a[_top - 1];}//获取数据个数int Size(){return _top;}//销毁void Destroy(){free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDateType* _a;int _top;int _capacity;
};//测试
//int main()
//{
//	Stack s;
//	s.Init();
//	s.Push(1);
//	s.Push(2);
//	s.Push(3);
//	s.Push(4);
//	s.Push(5);
//
//	while (!s.Empty())
//	{
//		STDateType top = s.Top();
//		s.Pop();
//		cout << top << endl;
//	}
//	return 0;
//}

而C++ 使用类来定义之后,数据和方法是一体的,可以通过访问限定符来限制数据和方法的权限(从外部是否可以拿到),也正是因为C++ 类的存在为这些函数定了一个”域“ ,故而不用加前缀来修饰函数名加以区分;

上面的代码,将数据定为私有就是为了避免”素质“不高的程序员,而C语言限制少,就需要使用C语言写代码的程序员素质要高;

就拿获取栈顶的数据为例,在C语言中可以不适用接口函数直接访问数据,而在C++ 中将数据设为了private ,就没有办法从类外部擅自获取数据,想要获取栈顶的数据就只有通过接口函数来获取;同样,也可以将函数设置为private ,即这个函数属于内部逻辑,不期望别人来使用这个函数,仅供自己内部使用即可;需要注意的是,如果使用者不清楚结构的底层实现而擅自拿了数据来实现一些自以为”很简单“的接口函数的功能,就很容易出错,例如:不同库中对于栈的_top 的实现可能不同,命名不同、指向的空间不同……


总结

1、class 为定义类的关键字,Stack 为类的名字,{} 中为类的主体,注意类定义结束的时候其后面的分号不可以省略。类体中的内容称为类的成员:类中的变量称为类的属性或成员变量,类中的函数称为类的方法或者成员函数定义在类里面的成员函数默认为inline 内联函数

2、C++一种实现封装的方式,用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性地将其接口提供给外部的用户使用;class 定义成员没有被访问限定符修饰时默认为private ,struct 默认为public;

3、类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用 :: 作为用域操作符指明成员属于哪个类域;类域影响的是编译的查找规则

4、编译器编译后,类的成员函数默认会在形参的第一个位置,增加一个当前类类型的指针,叫做this 指针编译器编译后,类的成员函数默认会在形参的第一个位置,增加一个当前类类型的指针,叫做this 指针

5、C语言是面向过程,即关注于过程实现的语言;而C++ 是面向对象,即更加关注对象和对象、类和类之间的关系;

相关文章:

  • 《代码整洁之道》第4章 注释 - 笔记
  • CentOS7.9安装OpenSSL 1.1.1t和OpenSSH9.9p1
  • 小结:BFD
  • Redis ssd是什么?Redis 内存空间优化的点都有哪些?embstr 和 row、intset、ziplist分别是什么?
  • LeetCode题解1297. 子串的最大出现次数
  • 大模型评测调研报告
  • 计算机网络 | 应用层(6) -- 套接字编程
  • 大模型基础(三):Llama3复现
  • Mac桌面幻灯片,Google文档,google硬盘和google等图标如何移除
  • Docker(二):docker常用命令
  • 2025系统架构师---解释器架构风格‌
  • Rust:安全与性能兼得的现代系统编程语言
  • 深入探索Python Pandas:解锁数据分析的无限可能
  • 【Java】分布式事务解决方案
  • 「Mac畅玩AIGC与多模态02」部署篇01 - 在 Mac 上部署 Ollama + Open WebUI
  • (MySQL)表的操作
  • Ant(Ubuntu 18.04.6 LTS)安装笔记
  • 「Mac畅玩AIGC与多模态03」部署篇02 - 在 Mac 上部署 Dify
  • Pydantic:校验器(@validator)、模型嵌套、模型继承
  • 使用cesium设置第一视角
  • 报告显示2024年全球军费开支增幅达冷战后最大
  • 伊朗国防部发言人:发生爆炸的港口无进出口军用物资
  • 民航局:预计五一假期民航旅客运输量创同期历史新高,将加强价格管理
  • 来论|如何看待韩企在美申请“饺子”专利
  • 大漠孤烟为何“直”?物理学家在唐诗中读出“不一样的美”
  • 第二十届华表奖提名名单公布,张译、王一博、马丽、郭帆等入围