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

保姆级教程:在Ubuntu 22.04上用VLLM+OpenWebUI部署DeepSeek-R1-14B(含Q8量化模型下载) 本文提供在Ubuntu 22.04上使用VLLM和OpenWebUI部署DeepSeek-R1-14B大语言模型的完整教程,包括环境配置、模型下载、VLLM参数调优及OpenWebUI集成。特别针对Q8量化模型优化部署流程,解决CUDA版本冲突等常见问题,适合开发者快速搭建本地AI对话系统。 阅读详情

目录

前言

一、类的定义

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;
	}

	//push
	void 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;
	}

	//pop
	void 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++ 是面向对象,即更加关注对象和对象、类和类之间的关系;

debian linux系统安装教程,Debian 10(Buster)安装过程图文详解 Debian 项目发布了它的最新稳定版 Debian 10,其代号是 “Buster”,这个发布版将获得 5 年的支持。Debian 10 可用于 32 位和 64 位系统。这个发布版带来很多新的特色,列出下面一些特色:引入新的 Debian 10 的主题 “FuturePrototype”新版本的电脑桌面环境 GNOME 3.30、Cinnamon 3.8、KDE Plasma 5.14、MAT... 阅读详情

相关推荐

Ansys Lumerical 启动时出现 Error: Could not connect to Ansys license server specified at 1055@ 的一种解决办法

考虑到 2020 版的时候是直接在全局变量里指定具体地址,但是 2024 版本是写成 1055@localhost ,并且之后在站里搜相关 Ansys 的问题都指向 1055 端口被占用,虽然问题不一样,但是本着死马当活马医,就按照。我最近在跑超透镜结构,听说2024版本现在有GPU加速可以用,就把Lumerical从2020升到2024版本,结果发现一开头的破解办法完全不一样了。一开始安装的时候以为2020没有删干净,导致2024版的安装一直锁着目标地址,解决办法参考。的办法来操作,在关闭了。

qq_51404184的博客 5753

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 242

vasp-自旋轨道耦合(SOC)计算步骤

在VASP中执行自旋轨道耦合(SOC)计算,具体的计算步骤如下: 结构优化获取CONTCAR文件 自洽计算(collinear normal VASP calculations)获取IBZKPT,CHGCAR和WAVECAR文件。 SCF+SOC计算,在此步骤中,将步骤2中的IBZKPT复制为KPOINTS,以便两个步骤具有相同的k网格,因此可以读取步骤2中的CHGCAR和WAVECAR文件。 能带结构计算,在此步骤中,将KPOINTS改为高对称点,读取步骤3中的CHGCAR和WAVECAR文件。

懒散的鱼与消失的猫 3万+

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 391

QT与Coin3D实现机器人的仿真

前言 Coin3D编程环境配置好后,开始下一步的机器人仿真任务。本文根据在研5轴机器人,完成三维模型生成与机器人的仿真。主要参考博主冯Jungle的博客,在此表示感谢。 ——2021.05.23 环境 Win10:VS2019、QT5.15.2、Coin3D、SolidWorks2016 机器人三维模型生成 目前广泛应用于因特网的VRML ( Virtual Reality Modeling Language,虚拟现实建模语言)为用户提供一种可参与的、并能对场景中对象主动做出反应的虚拟现实环境,现已成

焦露的博客 6907

Python实战:动态爬取国家地表水水质实时监测数据(附最新代码解析)

本文详细介绍了使用Python动态爬取国家地表水水质实时监测数据的实战方法。针对动态网站数据加载特点,重点解析了如何利用Selenium模拟浏览器操作、结合BeautifulSoup解析数据,并提供了最新的代码实现与常见问题解决方案,帮助开发者高效获取环境监测数据。

app77的博客 279

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 339

ESP32水墨屏日历实战:低功耗自动更新与HTTPS数据解析

本文详细介绍了ESP32水墨屏日历项目的开发实践,重点讲解了低功耗自动更新与HTTPS数据解析的实现方法。通过ESP32的自动休眠功能和水墨屏的零功耗特性,结合HTTPS请求安全获取网络数据,实现了高效节能的桌面日历应用。项目代码开源,适合物联网开发者参考。

xx56789的博客 777

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 70

微信小程序 - 详细实现打开跳转第三方 H5 网页链接,在微信小程序内直接跳转打开第三方网站链接详细教程(支持跳转公众号文章等,直接复制代码即可)

小程序跳转h5,小程序-Taro小程序中打开h5页面链接,...小程序跳转h5链接(web-view)_微信小程序,小程序如何跳转h5页面,小程序跳转到第三方H5网页,微信小程序跳转第三方H5的方法,小程序-实现怎么跳转打开H5网页链接(或跳转至公众号文章,打开第三方网站,放校验文件,怎么配置业务域名,校验文件验证域名,把校验文件放到对面网站,别人的服务器也打不开没权限怎么放呢,勾选不校验合法域名,txt,打不开别人的网页怎么办,真机调试打不开,无法跳转,有什么其他办法不放校验文件吗,后台配置,怎么

🏆 前端领域优质创作者 3万+

学Simulink——基于Simulink的输入-输出线性化精确转矩控制

本文详细介绍了基于Simulink的永磁同步电机(PMSM)输入-输出线性化(IOL)精确转矩控制方法。针对传统解耦控制在非线性转矩特性下的局限性,提出通过精确反馈线性化实现转矩指令与实际转矩的严格线性关系。文章系统阐述了IOL原理、PMSM控制器推导过程,并提供了完整的Simulink建模步骤,包括电机模型搭建、IOL控制器实现和闭环系统构建。通过仿真对比验证了IOL方法在转矩跟踪精度、动态响应和参数鲁棒性方面的显著优势,最后讨论了工程实现要点和扩展应用方向。该方法为高性能伺服系统开发提供了有效解决方案。

amy_mhd的博客 60

【C语言】字符与字符串函数

C语言中字符串为以\0结尾的字符数组,标准库函数基于此约定。本文解析字符串内存布局,详解ctype.h与string.h中字符分类、长度计算、拷贝、比较等函数的使用要点,强调长度判断与越界风险,最后通过命令行待办事项示例整合fgets、sscanf、strncpy、strcmp,展示实际应用中的关键细节。

记录在学习过程中学到的知识 347

企业数字化转型解决方案架构设计:一个五维全景视角

本文系统探讨了企业数字化转型的架构设计框架,提出五维协同模型:企业架构作为战略罗盘,业务架构聚焦价值创造,应用架构实现能力载体,数据架构提供智能燃料,技术架构奠定基础支撑。文章深入分析了各架构域的核心要素、设计原则及相互关系,并指出数字化转型需通过渐进路径,结合战略引领、技术赋能和组织变革,构建有机统一的架构体系。该框架为企业应对数字化复杂性提供了系统性方法论,强调从整体视角实现技术与业务的深度融合。

金牌架构师 1256

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 304

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 437

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 211

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C 语言 malloc() 详解与实战 Demo

本文详解C语言malloc()函数,涵盖其语法、内存原理及实战应用。通过动态整数、数组、结构体与链表的Demo,演示如何安全申请、使用与释放内存,强调避免内存泄漏与空指针检查的重要性,帮助开发者掌握动态内存管理核心技能。

tangPHP的博客 371

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 137

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 790

【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针

C++类与对象知识(面向过程和面向对象的初步认识,类的初步认识,类的访问限定符及封装,类的作用域、实例化,类的对象模型,this指针)

2601_96587588的博客 884

C++09_vector类复习

本文以10个核心问题为主线,系统解析C++ vector的底层机制:从基本概念、初始化方式、遍历方法,到size与capacity的区别、扩容策略(VS 1.5×,g++ 2×)、reserve与resize差异、中间增删的挪动原理、迭代器失效原因,直至二维vector的内存布局(含三指针结构)。通过“先会用、再懂扩容、最后看清对象结构”的递进逻辑,揭示vector作为动态数组的本质——连续内存、自动扩容、随机访问高效,但插入删除代价高,迭代器易失效。全文深入浅出,助读者摆脱“黑盒”认知,真正掌握其内存图景

lemo_n_的博客 337

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 345

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 390

头歌 数据结构——C&C++线性表实训

本系列任务围绕线性表的链式实现,依次完成顺序构建、逆序构建、排序插入、查找、删除、求长度及栈、队列、集合等应用。通过动态内存分配与指针操作,掌握单链表的基本操作与实际应用,涵盖插入、删除、遍历、搜索等核心功能,最终实现数据结构在典型场景中的灵活运用。

一名敲代码的苦13程序员 238

C++ vector 添加与删除元素的底层原理:erase 返回值与倒序遍历删除

vector是连续内存的动态数组,支持O(1)随机访问,但扩容时需重新分配内存并搬迁元素。push_back和emplace_back均摊时间复杂度为O(1),扩容策略(如2倍或1.5倍)确保总代价均摊;insert与erase在中间位置操作需移动元素,时间复杂度O(n),且使后续迭代器失效。删除指定元素推荐使用erase-remove惯用法或std::erase_if,避免逐个删除导致O(n²)复杂度。从后往前遍历可防止漏删,但最优解仍是高效惯用法。预估容量后调用reserve()可避免频繁扩容,提升性能

2402_85743155的博客 794

字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)

本题核心思想是:最少移动步数 = 总长度 - 可保留的最长连续子串长度。未移动字母在T中必须为连续子串,在S中需保持相对顺序(即子序列)。通过枚举T的所有连续子串起点,贪心匹配S中的对应字符,求出最长可保留长度L,答案即为n−L。时间复杂度O(n²),空间复杂度O(n),适用于n≤1000。

冻柠茶飞冰走茶 588
上一篇: C++ - #命名空间 #输入、输出 #缺省参数 #函数重载 #引用 # const 引用 #inline #nullptr
下一篇: C++ - 类和对象 #类的默认成员函数 #构造函数 #析构函数 #拷贝构造函数 #运算符重载函数 #赋值运算符重载函数
秦少游在淮海
博客等级 码龄3年 1071粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值