【C++】【类与对象知识整理】(二)

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

  • C和C++之间的区别
  1. C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
  2. C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
    在这里插入图片描述

二、类的引入

C语言结构体中只能定义变量在C++中,结构体内不仅可以定义变量也可以定义函数。比如:
之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。
故,类的引用也可以通过 C的结构体 进行过度

//C++兼容c中 struct 的用法
//C++ 升级 struct 升级成了类
//类和对象:
//1 个 类, 实例化 N 个对象
  • 类和结构体有什么区别嘛?

1.结构体只有数据,升级成类之后,也可以写函数

//以前c是这样写的
struct Stack
{
    int* _array;
    size_t _capacity;
    size_t _top;
};
void StackInit(struct Stack* ps,int n)
{}

  • 升级成类之后,我们就可以把方法往类里面去放啦!
    请添加图片描述
struct Stack
{
   void Init(int n = 4) //全缺省函数,
    {
        _array = (int*)malloc(sizeof(int) * capacity);
        if (nullptr == _array)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n;
        _size = 0;
    }

//成员变量
   int* _array;
   size_t _capacity;
   size_t _top;
};

在这里插入图片描述

C++升级struct升级成类
1.类里面可以定义函数
2.名称就可以代表类型

    struct Stack st1; //可以这么用因为C++兼容C
    Stack st2;   //最通常就是用这种表达方式
//调用函数
    st1.Init(100);
    st2.Init();

在这里插入图片描述

  • C的结构体是这样定义的
typedef struct ListNode_C
{
  struct ListNode_C* next;
  int val;
}LTNode;
  • C++类是这样定义的
struct ListNodeCPP
{
    ListNodeCPP* next;  //结构体的名称就可以代表类型
    int val
};
//也体现了C++是兼容C的struct的用法

在这里插入图片描述


三、类的定义

接下来!我们就写一个正儿八经的C++的类吧!
Let’s Go! !!👇 请添加图片描述
class className  //其实也是改了一个名字
{
// 类体:由成员函数和成员变量组成
    void Init(int n = 4) //全缺省函数,
     {
         _array = (int*)malloc(sizeof(int) * capacity);
         if (nullptr == _array)
         {
             perror("malloc申请空间失败");
             return;
         }
         _capacity = n;
         _size = 0;
     }

//成员变量
    int* _array;
    size_t _capacity;
    size_t _top;
};  // 一定要注意后面的分号

----->请添加图片描述感觉有点东西了?但是下面引出了一个问题!

  • 当我们直接调用类里面的函数时,发生报错啦!
int mian()
{ 
    Stack st1;
    st1.Init();
}

在这里插入图片描述

  • 其实C++还引入一个东西,叫做: 访问限定符!在这里插入图片描述

四、类的访问限定符及封装

4.1 访问限定符

  • 保护限定符有三种!

请添加图片描述

⭐公有–>在类外面可以直接访问(其他则不可以)

欸?所以为什么会出现上面的报错呢!

  • class的默认访问权限为private,struct为public(因为struct要兼容C)

👉想要访问成员变量也很简单!超级简单

class classname
{
public:
    //成员函数
    //这里的成员就会变成公有的了!
private:
    //成员变量
}

总结:一般情况下,成员变量设置为私有;而成员函数会设置为公有 在这里插入图片描述

4.2 封装

  • 面向对象的三大特性:封装、继承、多态

封装其实就是通过设置公有私有来对对象进行更好的管理
在这里插入图片描述


五、类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。

在这里插入图片描述

====Stack.h👇====

class Stack
{  //花括号括起来的基本都是域
public:
	void Init();	
private:
	int* _a;
	int _top;
	int _capacity;

};

====Stack.cpp👇====

#include"Stack.h"
void Init() 
{
	_a = nullptr;
	_top = 0;
	_capacity = 0;
}

在这里插入图片描述
我们看一下报错的结果!

在这里插入图片描述

  • 结果:其实是因为编译器不知道 Init 要访问的是哪里,所以我们只要在前面加上 Stack:: 就可以解决这个问题了

在这里插入图片描述

  • 通过以上例子:我们提出了类域这个概念
  • 当声明和定义分开的时候,我们就需要 :: 指定作用域

总结:我们现在一共学习了四个域!

  1. 局部域
  2. 全局域
  3. 命名空间域
  4. 类域

在这里插入图片描述


六、类的实例化

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

int main()
{
    //类 -> 对象   一对多的关系
	//对象的实例化
    Stack st1;
    Stack st2;
}

在这里插入图片描述
在这里插入图片描述

 Stack 实例化的对象,调用成员函数地址都是一样的
 每个对象内部都放一份,大大的浪费
 1.代码只保存一份,在对象中保存存放代码的地址
 2.只保存成员变量,成员函数存放在公共的代码段

七、类对象模型

-

但是,如果是空类的话,大小又是多少呢!

// 空类的大小为 ——> 1
// 这一个字节,不存储有效数据,
//标识对象被定义出来了程序
class A3
{};

结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐 注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

  • 什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景
/*
    什么是大小端:
    大端:数据的低位字节内容保存在内存的高地址出
         较低 的有效字节存放在 较高 的存储器地址
         较高 的有效字节存放在 较低 的存储器地址

    小端:较低 的有效字节存放在 较低 的存储器地址
         较高 的有效字节存放在 较高 的存储器地址
*/
#include<stdio.h>


int main()
{
	int i = 1;
	char ret = *((char*)&i);
	//获取i存储的内存地址指针,将其转换为char*类型,提取第一个字节地址指针
	//再将获取得到的地址指针进行解引用
	//1在内存中存储为0x000001
	//如果第一个字节为0,说明机器是大端存储,低位字节1存储在高位地址
	//如果第一个字节为1,说明机器是小端存储,低位字节1存储在低位地址
	if (ret == 0)
	{
		printf("大端存储");
	}
	else
	{
		printf("小端存储");
	}
}


/*

int check_sys()
{
	union Un
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;//返回1 就是小端
				//返回0 就是大端
}
int main()
{
	int ret = check_sys();
	if (ret == 0)
	{
		printf("大端存储");
	}
	else
	{
		printf("小端存储");
	}
	return 0;
}
*/

在这里插入图片描述


八、this指针

  • 隐含的this指针:
  • 实参和形参的位置不能显示写,因为编译器会自己加
  • 但是在类里面可以用

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

答案: C

  • 不需要在P的对象里面找print的地址
  • 传递一个空指针不会报错,因为没有对空指针访问

在这里插入图片描述

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

答案:B

在这里插入图片描述

原因分析👇

在这里插入图片描述在这里插入图片描述

附加内容

  • this 存在栈上面 因为他是形参
  • 形参和局部变量都是存在栈上面的

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

  • 在用C语言实现时,Stack相关操作函数有以下共性:
  1. 每个函数的第一个参数都是Stack*
  2. 函数中必须要对第一个参数检测,因为该参数可能会为NULL
  3. 函数中都是通过Stack*参数操作栈的
  4. 调用时必须传递Stack结构体变量的地址

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。

在这里插入图片描述

C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在
类外可以被调用,即封装
,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,
即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。


在这里插入图片描述

请添加图片描述

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值