【C++初阶】三、类和对象(面向过程、class类、类的访问限定符和封装、类的实例化、类对象模型、this指针)

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【C++初阶】二、入门知识讲解
(引用、内联函数、auto关键字、基于范围的for循环、指针空值nullptr)-CSDN博客

 =========================================================================

                     

一 . 面向过程和面向对象初步认识

C语言 -- 面向过程

                    

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

                       
  • 例子
    假设要完成洗衣服这个目标通过C语言完成就需要分出多个过程,如:
    拿盆子  =>  放水  =>  放衣服  =>  放洗衣粉  =>  手搓  =>  换水  =>  再放洗衣粉  =>  
    再手搓  =>  拧干衣服  =>  晾衣服

                     

                     


                    

C++ -- 面向对象

                 

  • C++面向对象的,关注的是对象一件事情拆分成不同的对象
    依靠对象之间的交互解决问题
                       
  • 例子
    同样是完成洗衣服这个目标通过C++完成就需要找出需要的相应的对象
    洗衣服需要的对象有衣服洗衣粉洗衣机
    然后就可以依靠对象的交互完成整个洗衣服的过程
    衣服放进洗衣机  =>  倒入洗衣粉  =>  启动洗衣机 清洗衣服并甩干
    可以看到整个过程是通过我们定义的四个对象之间的交互完成的
    不需要关心洗衣机具体是如何操作的

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

二 . 类的引入 --  struct类

                   

C语言struct结构体只能定义变量
C++中strcut结构体不仅可以定义变量还可以定义函数

例如

之前在数据结构初阶中,用C语言方式实现的栈

【数据结构初阶】五、线性表中的栈
(C语言 -- 顺序表实现栈)_高高的胖子的博客-CSDN博客

这里实现的栈Stack结构体只能定义变量

而如果以C++方式实现栈的话,会发现Stack结构体还可以定义函数

图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

三 . 类的定义 --  class类

class类的介绍:

                  

  • C++中,虽然可以使用struct来定义类,但还是更喜欢通过class来定义类
                  
图示:
                            class className
                            {
                                //类体:由成员变量和成员函数组成
        
                            }; //和struct一样最后需要加上分号
  • class定义类的关键字className类的名字
    { } 中为类的主体类定义结束时最后的分号不能省略
                  
  • 类体中的内容称为类的成员
    类中的变量称为类的属性成员变量类中的函数称为类的方法或者成员函数
                 
  • 定义类的成员变量时一般会在成员变量名前加上_” ,表示该变量为类内部的成员变量
图示:

                     

                     


                    

class类的两种定义方式:

           

第一种:成员函数的声明和定义都在类体中

成员函数如果在类中定义编译器可能会将其当成内联函数处理

图示:

                          

                          
---------------------------------------------------------------------------------------------

                    

第二种:类声明放在头文件中,成员函数实现放在 .cpp文件 中


  • .cpp文件 中,需要通过类名作用域限定符::指定实现类中的某个函数
                     
  • 类的作用域
    定义了一个新的作用域类的所有成员都在类的作用域
    类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域
图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

四 . 类的访问限定符和封装

访问限定符:

                          

  • C++实现封装的方式
    对象的属性与方法结合在一块,让对象更加完善

    通过访问权限通过访问限定符管理权限选择性的将其接口提供给外部的用户使用

                        

访问限定符有三个:

  • public公有)、protected保护)、private私有
                    
  • public公有):被public修饰的成员类外类内都可以直接被访问
                   
  • protected保护)和  private私有):
    被修饰的成员在类外不能直接被访问,只能在类内被访问

注:

类内外访问这方面这两个访问限定符类似
当前阶段可以认为protectedprivate是没有区别的
之后
了解了继承才能够知道两者的区别

                          

                          
---------------------------------------------------------------------------------------------

                    

访问权限作用域范围:

  • 访问权限作用域该访问限定符出现的位置开始直到下一个访问限定符出现为止
    如果后面没有再出现过访问限定符,则作用域就到}为止,即类结束
                         
  • class默认访问权限privatestruct的为public(因为struct要兼容C语言
注:

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

                  

图示:

                          

                          
---------------------------------------------------------------------------------------------

                    

C++中struct和class的区别:

C++需要兼容C语言,所以C++中struct除了可以当成使用还可以当成结构体使用
struct定义的类class定义的类区别

struct定义的类默认的访问权限publicclass定义的类默认的访问权限private

注:

继承模板参数列表位置structclass也有区别,后面再进行了解

                     

                     


                    

封装:

                  

  • 面向对象三大特性封装继承多态
    类和对象阶段主要研究的是类的封装特性
                     
  • 封装
    数据操作数据的函数方法)进行有机结合隐藏对象的属性实现细节
    仅对外公开接口来和对象进行交互
                  
  • 封装本质是一种管理让用户能够更方便使用类,例如:
    对于电脑这样一个复杂的设备提供给用户的就只有一个开关机键
    通过键盘输入显示器USB插孔等,用户计算机进行交互,完成日常事务。
    实际上电脑真正工作的却是CPU显卡内存等一些硬件元件
                           
  • 对于计算机使用者而言,不用关心内部核心部件
    比如主板上线路是如何布局的、CPU内部是如何设计的等等。
    用户只需要知道
    怎么开机怎么通过键盘和鼠标与计算机进行交互即可。
    因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来
    仅仅对外提供开关机鼠标以及键盘插孔等,让用户可以与计算机进行交互即可
                      
  • 要在C++语言中实现封装
    可以通过数据以及操作数据的函数方法进行有机结合
    通过访问权限隐藏对象内部实现的细节控制哪些函数可以在类外部直接被调用

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

五 . 类的实例化

  • 类类型创建对象的过程,称为类的实例化
                  
  • 是对对象进行描述,是一个模型一样的东西限定了类有哪些成员
    仅定义出一个类时并没有分配实际的内存空间来存储它
    例如
    建造房子时需要的设计图设计图就可以看成是一个
    通过设计图可以设计建造出多个类似的房子
    这里多个类似的房子可以看成是设计图类的多个对象
    设计图只设计出需要什么东西,但是并没有实体的建筑存在
    同样也只是一个设计实例化出的对象才能实际存储数据占用物理空间
                     
  • 一个类可以示例化出多个对象实例化出的对象占用实际的物理空间
    存储类成员变量,但没有存储类成员函数
    因为同一个类的多个对象调用的是相同的成员函数,所以成员函数会存放在公共代码区
图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

六 . 类对象模型

计算类对象的大小:

               

中既可以有成员变量又可以有成员函数

那么一个类的对象中包含了什么?又要如何计算一个类的大小呢?

            

类对象的存储方式:

  • 一个中可能有成员变量或者成员函数

    实际上类对象只有类的成员变量,而没有成员函数

    因为成员函数可以被多个类对象共用,所以成员函数是存放在公共代码区中的

    每个类对象的成员变量则是独立

                     
  • 所以一个类对象的大小实际就是该类中成员变量之和
    计算其大小时,需要用到和计算结构体大小同样的方法内存对齐
                
  • 一个中如果没有成员变量则称该类为空类计算空类大小会比较特殊
    编译器给了空类对象一个字节来唯一标识这个空类的对象
图示:

                     

                     


                    

结构体内存对齐规则:

            

计算对象大小需要了解关于内存对齐的知识
关于内存对齐之前有博客详细介绍过

学C的第三十天【自定义类型:结构体、枚举、联合】_高高的胖子的博客-CSDN博客

                  

内存对齐简单回顾:

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

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

七 . this指针

this指针的引入:

              

先定义一个日期类Date:
//日期类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;
    Date d2;

    d1.Init(2023, 10, 7);
    d2.Init(2022, 10, 7);

    d1.Print();
    d2.Print();

    return 0;
}
  • 对于上述类,有这样一个问题:

    Date类中有 Init Print 两个成员函数函数体中没有关于不同对象的区分

    当 d1对象 调用 Init成员函数 时
    该函数是如何知道应该设置 d1对象而不是设置 d2对象
                   

  • C++通过引入 this指针 来解决该问题
    即:
    C++编译器给每个非静态的成员函数增加了一个隐藏的this指针参数
    该指针指向当前对象函数运行时调用该函数的对象),
    在函数体中所有成员变量的操作都是通过该指针去访问
    只不过所有的操作对用户透明用户不需要自己传递编译器自动完成

                    

this指针的特性:

  • this指针类型类类型* const ,即成员函数中不能给this指针赋值
               
  • 只能在成员函数内部使用
                  
  • this指针本质是成员函数形参,当对象调用成员函数时,
    对象地址作为实参传递给this形参所以对象中不存储this指针
                       
  • this指针是“成员函数第一个隐藏的指针形参
    一般情况由编译器通过ecx寄存器自动传递不需要用户传递
                      
  • this指针是一个形参是个局部变量是存放在栈帧上面的
    VS编译器中,this指针被存放在ecx寄存器
图示:

                     

                     


                    

C语言和C++实现类的对比:

            

C语言实现类(型):

C语言实现一个类型(结构体)时,该类型相关操作函数有以下共性:
  • 每个相关操作函数第一个参数都是接收该类型结构体变量的指针
    获取该类型底层结构
                          
  • 函数中必须要对第一个参数(“底层结构”)进行检测因为该参数可能会为NULL
                      
  • 函数中都是通过第一个参数(“底层结构”)操作该类型结构体
                 
  • 调用函数时必须传递该类型结构体变量的地址
                        

结构体中只能定义存放数据的结构操作数据的方法不能放在结构体中

数据操作数据的方式分离开,而且实现上相对会复杂一些

涉及到大量指针操作稍不注意可能就会出错

                          

                          
---------------------------------------------------------------------------------------------

                    

C++实现类:

C++中通过可以将 数据 以及 操作数据的函数方法进行完美结合

通过访问权限还可以控制那些方法在类外是否可以被调用,即封装

在使用时就像使用自己的成员一样,更符合人对一件事务的认知。

而且每个成员函数成员方法不需要传递像C语言中的第一个参数(“底层结构”),

编译器编译之后该参数自动还原通过隐藏的this指针),

C++中的“底层结构参数是由编译器维护的,而C语言中需要用户自己维护

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

本篇博客相关代码:

​​​​​​Stack.h -- 头文件:

#pragma once

//类头文件:

class Stack
{
private: //这是访问修饰符下一标题处会了解到
	//成员变量:
	int* a;
	int top;
	int capacity;

public: //同样是访问修饰符
	//成员函数:
	void Init(); //栈初始化函数(方法)-- 声明

	void Push(int x); //出栈函数(方法)-- 声明

	/*
	* 成员函数可以分文件实现,
	* 也可以直接就在头文件中实现,
	* 但这样的直接在类中实现函数的话,
	* 该函数会被默认为是内联函数(inline),
	* (虽然是内联函数,但编译器确定是否展开)
	* 
	*		所以正确的用法是:
	* 长函数的声明和定义要分离(分文件实现),
	* 短函数可以直接在类中就进行声明
	*/
	bool Empty()
	{
		return top = 0;
	}
};

                     

                     


                    

Stack.cpp -- C++文件

#define _CRT_SECURE_NO_WARNINGS 1

//包含类头文件:
#include "Stack.h"

//类函数(方法)实现文件:

/*
* 通过命名空间(类名)和作用域限定符,
* 来指定实现类中对应的函数(方法)
* (类定义的也是一个域)
*/

//栈初始化函数(方法)-- 实现
void Stack::Init() 
//指定实现Stack类中的Init函数(方法)
{
	a = 0;
	top = 0;
	capacity = 0;

	//a、top、capacity都是栈的成员变量
}

//出栈函数(方法)-- 实现
void Stack::Push(int x) 
//指定实现Stack类中的Push函数(方法)
{
	//…… 
}

                     

                     


                    

Test.cpp -- C++文件

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <iostream>
using namespace std;

//使用C++实现一个栈:

//C语言中实现栈:
//struct Stack
//{
//	int* a;
//	int top;
//	int capacity;
//};
//
C语言中栈相应的函数:
//void StackInit(struct Stack* ps);
//void StackPush(struct Stack* ps, int x);
//……

/*
* 由此可见:C语言中栈的数据和方法是分离的
* 数据 -- struct Stack
* 方法 -- StackInit、StackPush等等
*		(C语言 -- 面向过程)
* 
* C++中:兼容C语言struct的所有用法,
* 不仅兼容,而且还将struct升级成了“类”
*/

//C++中实现栈:
//struct Stack
//{
//	int* a;
//	int top;
//	int capacity;
//
//	/*
//	* 2、类中可以定义与该类相关的函数(方法)
//	* 
//	* (1)不需要像C语言中将数据和函数(方法)分离。
//	* 
//	* (2)因为函数(方法)被包含在类中,
//	* 所以不再需要像C语言中必须将函数定义在全局中,
//	* 所以函数名不需要定义得像 StackInit(堆的初始化函数)一样,
//	* 来特指是谁的初始化函数,直接在类中定义为 Init 即可,
//	* 该 Init函数 在Stack(堆)类的域中,
//	* 就是只属于Stack的Init函数
//	*/
//
//	void Init()
//	{
//		a = 0;
//		top = 0;
//		capacity = 0;
//	}
//
//	void Push(int x)
//	{
//		//……
//	}
//
//};


//int main()
//{
//	//C语言中调用struct“类型”:
//	struct Stack s1;
//
//	//C++中调用struct“类”:
//	Stack s2; 
//	/*
//	* 1、类名就是类型,Stack就是类型,不需要加struct
//	* struct后的名称就是类名,要调用类,直接调用类名即可,
//	* 不用加struct
//	*/
//	
//	//C++中:
//	//调用类中的变量或者函数的方法
//	//和调用结构体成员的方法一样:
//	s2.Init(); //调用栈类中的Init函数 (C++)
//
//	//调用栈类中的出栈Push函数(C++):
//	s2.Push(1);
//	s2.Push(2);
//	s2.Push(3);
//	s2.Push(4);
//
//	//C语言中:
//	struct Stack s1; 
//	//声明时还需加struct(不加typedef)的话
//
//	//调用的是全局中的函数:
//	StackInit(&s1);
//	StackPush(&s1, 1);
//	StackPush(&s1, 2);
//	StackPush(&s1, 3);
//	
//	return 0;
//}

//假设定义一个链表结点类:
struct ListNode
{
	ListNode* next; 
	/*
	* 这里可以直接使用类名类型来定义变量了,
	* 而不是C语言中的:struct ListNode* next; 
	*/
	
	int val;
};


//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//private:
//	/*
//	* C++中一般会在成员变量前加一个 “_” ,
//	* 表示该变量为类内部的成员变量,
//	* 防止在成员函数调用时和形参命名冲突
//	*/
//	
//	int _year;
//	int _month;
//	int _day;
//
//};

//int main()
//{
//	Date d; 
//	
//	d.Init(2023, 10, 17);
//
//	return 0;
//}



/*
* C++中,虽然可以使用struct定义类,
* 但还是更喜欢通过class来定义类,
* 
* class中由两部分构成:
* 变量(成员变量)和函数(成员函数),
* 两者统称类的成员
* 
* 类会通过访问限定符来实现封装,
* 访问限定符分为:
* public(共有)、protected(保护)、private(私有)
* 
* public(共有):类中和类外都可以进行访问
* protected(保护):类中可以访问,类外不能访问
* private(私有):类中可以访问,类外不能访问
* 
* 在当前阶段,可以认为protected和private是没有区别的,
* 等后面了解了继承才能够知道两者的区别
* 
*			struct 和 class 的区别:
* 1、class的默认访问权限为private,但实践中建议还是明确写上限定符
*	 struct默认访问权限为public(为了兼容C语言)
* 2、(其它就没有什么大的区别)
* 
*			C++中设置访问限定符的目的:
* C语言中没有访问限定符的概念,数据和方法是分离的,
* 有时实现一个目标可以通过数据完成,也可以通过方法完成,
* 程序员素养比较高的话应该是使用方法(函数)完成,这是比较规范的
* 
* 所以C++中类的成员默认是private私有的,无法在外部调用数据,
* 在解决一个问题时就只能通过在类中定义方法(函数)来完成,
* 提高代码的规范性
*/

//class Stack
//{
//private: 
//	//私有:让以下三个成员变量的权限为私有
//	int* a;
//	int top;
//	int capacity;
//
//public:
//	//共有:让以下的两个成员函数的权限为共有
//	void Init()
//	{
//		a = 0;
//		top = 0;
//		capacity = 0;
//	}
//
//	void Push(int x)
//	{
//		//……	
//	}
//
//	bool Empty()
//	{
//		return top == 0;
//	}
//
///*
//* 一个访问限定符的作用范围为:
//* 如果后面还有限定符 -- 当前限定符到下个限定符
//* 如果后面没有限定符 -- 当前限定符到 “}” 
//*/
//};

//int main()
//{
//	Stack s1;
//	 
//	//权限为共有的成员可以在类外部进行调用:
//	s1.Init(); //Init函数为共有
//	s1.Push(1); //Push函数为共有
//	s1.Push(2);
//	s1.Push(3);
//	s1.Push(4);
//
//	//权限为私有或保护的成员不可以在类外部进行调用:
//	s1.a = 0; //成员变量a为私有
//}


//类的实例化:

//C++中 “{}” 定义的都是域

//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//private:
//	int _year;
//	int _month;
//	int _day;
//	//这里这些成员变量只是声明,还没有开辟空间
//	/*
//	* 变量是否定义(实现)要看是否有开辟空间
//	* (在内存中开辟空间)
//	*/
//};

/*
* 类 和 对象  -->  一对多的关系
* 一个类可以有多个对象,可以想象类是设计图,
* 对象是通过设计图建出来的房子,一个设计图
* 可以设计建出多个类似的房子
*/


//class A
//{
//private:
//	char _ch;
//	int _a;
//}; //内存对齐问题


class B
{}; 
//没有成员变量的类(空类)的大小:1
/*
* 没有成员变量的类,说明该类不需要存储数据,
* 虽然该类没有成员变量,但还是可以创建该类的对象,
* 为了要表示一个空类的对象,证明空类B的对象存在,
* 就需要为这个空类对象开一个字节大小的空间,
* 这个字节不存储有效数据,仅标识定义的对象存在过
*/


class C
{
public:
	void f()
	{};
};
//有成员函数没有成员变量的类的大小:1
/*
* 该类还是没有成员变量,所以本质还是一个空类,
* 虽然有成员函数,但成员函数并不存放在该类中,
* 而是存储在公共代码区中,所以该类大小为1个字节
*/



//int main()
//{
//	Date d1; //定义(实例化)一个对象
//	Date d2; //再定义(实例化)一个对象
//	Date d3; //再再定义(实例化)一个对象
//
//	/*
//	* 定义一个对象后,对象中的成员变量
//	* 作为对象一部分,一起开辟了空间,
//	* 这时成员变量才被定义(实现)了
//	*/
//
//	d1.Init(2023, 10, 7);
//	d2.Init(2022, 10, 7);
//	/*
//	* (同个类)不同对象的成员函数是一样的:
//	* 这里d1和d2调用的是同一个函数
//	*(汇编指令call调用的是同一函数地址)
//	*/
//
//	//假设将成员变量的权限设置为public:
//	d1._year++;
//	d2._year++;
//	/*
//	* (同个类)不同对象的的成员变量是不一样的:
//	* 这里 d1的_year 和 d2的_year 不是同一个
//	*/
//
//	return 0;
//}


//int main()
//{
//	Date d1; //定义(实例化)一个对象
//	Date d2; //再定义(实例化)一个对象
//	Date d3; //再再定义(实例化)一个对象
//
//	//使用sizeof计算类的大小:
//	cout << sizeof(d1) << endl; //8个字节
//	/*
//	* sizeof计算类对象的大小和
//	* sizeof计算结构体大小是一样的,
//	* 所以要考虑内存对齐
//	* 
//	* sizeof计算类对象的大小时,
//	* 只会计算成员变量的大小,再考虑内存对齐,
//	* 不会考虑成员函数的大小
//	* 
//	* 成员函数不在对象里面,因为同一个类的不同对象
//	* 调用的是同一个成员函数(同名函数的情况下),
//	*(汇编指令call调用的是同一函数地址)
//	* 但同一个类的不同对象各自的成员变量是独立的,
//	* 类对象d1中的成员变量 和 类对象d2中的成员变量
//	* 是不同的,所以sizeof计算类对象时计算的是其成员变量
//	* 
//	* 成员变量存在对象中,而成员函数不存在对象中的原因:
//	* 成员变量各个对象不同(独立),
//	* 但成员函数调用的都是同一个,
//	* 所以没必要在所有对象中都存成员函数的地址,
//	* 将其放在一个公共的区域(公共代码区)是更适合的,
//	* 公共代码区在编译完成后是一堆指令,
//	* 编译链接时就可以确定函数的地址
//	*/
//	cout << sizeof(A) << endl;  //A类大小:8个字节
//	cout << sizeof(B) << endl;  //B类大小:1个字节
//	cout << sizeof(C) << endl;  //C类大小:1个字节
//
//	return 0;
//}


class Date
{
public:
	//Date类初始化函数:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//Data类打印日期函数:
	void Print()
	{
		cout << _year << "_" << _month << "_" << _day << endl;
	}
		/*
		* d1 和 d2 都调用该成员函数,但结果却不同的原因:
		* 
		* 我们写的 void Print()函数,编译器编译时实际上是这样的:
		* void Print(Date* const this),有一个隐藏的参数--this指针
		* (所有我们定义的成员函数都默认有一个this指针参数)
		* 
		* d1调用函数时,访问的是: &d1->_year、&d1->month、&d1->_day
		* 此时 this指针 就是 d1的指针 ,&d1是实参,Date* this是形参
		* 访问的是d1对象的_year、_month、_day
		* 
		* d2调用函数时,访问的是: &d2->_year、&d2->month、&d2->_day
		* 此时 this指针 就是 d2的指针 ,&d2是实参,Date* this是形参
		* 访问的是d2对象的_year、_month、_day
		* 
		* this指针是形参,是个局部变量,是存放在栈帧上面的
		* (VS编译器中,this指针被存放在ecx寄存器中)
		*/
	//编译器编译时的Print函数:
	void Print(Date* const this)
	{
		cout << this->_year << "_" << this->_month << "_" << this->_day << endl;
		/*
		* 注:不能显式写出this相关实参和形参,但可以在成员函数中显式写上this指针
		*(之后有地方会需要显式地在成员函数中写出this指针)
		*/
	}

private:
	int _year;
	int _month;
	int _day;

};

class A
{
private:
	char _ch;
	int _a;
};

int main()
{
	Date d1; //定义(实例化)一个对象
	Date d2; //再定义(实例化)一个对象

	d1.Init(2023, 10, 7);
	d2.Init(2022, 10, 7);

	d1.Print();
	d2.Print();
	
	//编译器编译时的426和427代码:
	d1.Print(&d1); //传送“this”指针
	d2.Print(&d2); //传送“this”指针

	return 0;
}


//对象用 “.” 访问成员;指针用 “->” 访问成员


/*
*				1、
* C语言数据和函数(方法)是分离的,
* 而C++通过类将数据(成员变量)和成员函数(成员方法)绑定在一起
* 
*				2、
* C语言调用函数时,需要将类型变量传给函数,
* 而C++因为有this指针的存在,不需要传类对象给成员函数
*/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【力扣题:循环队列】

文章目录 一.题目描述二. 思路解析三. 代码实现 一.题目描述 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好…

Oracle-动态sql学习笔记,由易至难讲解七个例子

本文章的内容来源于对oracle课堂上讲的内容做出的笔记 静态sql和动态sql 静态sql&#xff1a; 静态 SQL 是在编译时写死的 SQL 语句&#xff0c;即在程序编写阶段&#xff0c;SQL 语句已经被固定下来。 特点&#xff1a; 1.预编译&#xff1a; SQL 语句在程序编译时就会被…

C#中.NET 7.0 Windows窗体应用通过EF访问新建数据库

目录 一、 操作步骤 二、编写EF模型和数据库上下文 三、移植&#xff08;Migrations&#xff09;数据库 四、编写应用程序 五、生成效果 前文已经说过.NET Framework4.8 控制台应用通过EF访问已经建立的和新建的数据库。 前文已经说过.NET 6.0 控制台应用通过EF访问…

ajax异步传值以及后端接收参数的几种方式

异步传值 第一种呢&#xff0c;也是最简单的一种&#xff0c;通过get提交方式&#xff0c;将参数在链接中以问号的形式进行传递 // 前台传值方法 // 触发该方法调用ajaxfunction testAjax(yourData) {$.ajax({type: "get", // 以get方式发起请求url: "/yo…

海康设备、LiveNVR等通过GB35114国密协议对接到LiveGBS GB28181/GB35114平台的详细操作说明

一、LiveNVR通过GB35114接入LiveGBS 1.1 开启LiveGBS 35114功能 信令服务livecms.ini配置文件中[sip]增加一行gm1 启动LiveCMS 1.2 生成设备端证书 我们用LiveNVR做为设备端向LiveGBS注册&#xff0c;这里先生成LiveNVR的设备证书&#xff0c;并将LiveNVR的设备证书给LiveGB…

绩效考核管理项目|记录1

项目用C#winformSQL Server写的&#xff0c;现在记录一下学习到的新东西。 winform工具 splitContainer&#xff1a;分割出两个容器&#xff0c;能添加面板之类的工具 treeview&#xff1a;展示标签页的分层集合&#xff08;用户管理、基数管理......&#xff09;&#xff0…

GZ038 物联网应用开发赛题第6套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第6套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

蓝桥杯 大小写转换

islower/isupper函数 islower和issupper是C标准库中的字符分类函数&#xff0c;用于检查一个字符是否为小写字母或大写字母 需要头文件< cctype>,也可用万能头包含 函数的返回值为bool类型 char ch1A; char ch2b; //使用islower函数判断字符是否为小写字母 if(islower(…

短路语法 [SUCTF 2019]EasySQL1

打开题目 输入字符的时候啥也不回显。只有输入数字的时候页面有回显 但是当我们输入union&#xff0c;from&#xff0c;sleep&#xff0c;where&#xff0c;order等&#xff0c;页面回显nonono&#xff0c;很明显过滤了这些关键词 最开始我的思路是打算尝试双写绕过 1;ununion…

【SpringBoot】序列化和反序列化介绍

一、认识序列化和反序列化 Serialization&#xff08;序列化&#xff09;是一种将对象以一连串的字节描述的过程&#xff1b;deserialization&#xff08;反序列化&#xff09;是一种将这些字节重建成一个对象的过程。将程序中的对象&#xff0c;放入文件中保存就是序列化&…

220V交流转直流的简易电源设计

220V交流转直流的简易电源设计 设计简介设计原理电路图变压器电路交流转直流电路3.3V电源接口电路 PCB3D图 实践检验 设计简介 通过模拟电路的相关知识&#xff0c;尝试将220V的交流电转化为我们指定电压的直流电。 设计原理 将220V交流电转化为直流电的方法常用的有通过变压器…

京东数据挖掘(京东数据采集):2023年Q3电脑行业数据分析报告

近年来&#xff0c;在远程办公、远程教育等需求的刺激下&#xff0c;电脑的销售增长较为显著。不过&#xff0c;随着市场的成熟乃至饱和&#xff0c;电脑销售市场也逐渐出现增长困难、需求疲软等问题。 2023年第三季度&#xff0c;电脑市场的出货量同比下滑。根据鲸参谋电商数据…

lc121. 买卖股票的最佳时机

一次遍历&#xff0c;一边遍历一边修改买入的价格&#xff0c;一边比较取得最大利润 public class BuyAndSellStocks {public static void main(String[] args) {int[] arr {7,1,5,3,6,4};int[] arr1 {7,6,4,3,1};System.out.println(buyAndSellStocks(arr));System.out.pri…

【开发问题解决方法记录】01.dian

一些问题记录 新增角色失败&#xff1a;Error: Ajax 调用为Execute Server-Side Code返回了服务器错误ORA-01722: 无效数字。 【问题原因】&#xff1a;CREATE_BY(NUMBER类型)应该存入USER_ID(NUMBER类型)而非USER_NAME&#xff08;NVARCHAR2类型&#xff09; 【解决方法】将…

碾压Fast Request!IDEA插件推荐:Apipost-Helper

IDEA是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。我们在编写完接口代码后需要进行接口调试等操作&#xff0c;一般需要打开额外的调试工具&#xff0c;而今天给大家介绍一款IDEA插件&…

第十九章绘图

Java绘图类 Graphics 类 Grapics 类是所有图形上下文的抽象基类&#xff0c;它允许应用程序在组件以及闭屏图像上进行绘制。Graphics 类封装了Java 支持的基本绘图操作所需的状态信息&#xff0c;主要包括颜色、字体、画笔、文本、图像等。 Graphics 类提供了绘图常用的方法&a…

阿里云轻量级服务器搭建

购买服务器 服务器分类 轻量应用服务器 阿里云ECS 购买轻量应用服务器 实例类型 地域和可用区 *这里选择的是服务器的所在地&#xff0c;有国外和国内的 镜像 系统镜像 *系统镜像就是服务器自身的系统类型 *这里建议选择Centos7.x版本的 应用镜像 *用镜像就是在系统…

C语言——分割单向链表

本文的内容是使用C语言分割单向链表&#xff0c;给出一个链表和一个值&#xff0c;要求链表中小于给定值的节点全都位于大于或等于给定值的节点之前&#xff0c;打印原始链表的所有元素和经此操作之后链表的所有元素。 分析&#xff1a;本题只是单向链表的分割&#xff0c;不涉…

JavaScript 语句、标识符、变量

语句 JavaScript程序的单位是行(line),也就是一行一行地执行。一般情况下&#xff0c;每一行就是一个语句 var num 10; 语句以分号结尾&#xff0c;一个分号就表示一个语句结束。 标识符 标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名标识符…

如何快速将钉钉员工信息同步到飞书

当企业内部在使用钉钉跟飞书时&#xff0c;那么当钉钉员工信息发生更改时&#xff0c;我们应该如何将信息快速同步到飞书上呢&#xff0c;接下来我们借助RestCloud AppLink平台进行演示。 第一步&#xff1a;获得钉钉以及飞书认证授权 钉钉授权 钉钉接入采用自建应用的方式&…