类和对象----上 表层过度

前言须知:

类和对象我会分三节学完,这章,类和对象(上),在这节会主要理解 什么是类,类域,类中各种特点名词的意思,c++和c语言中struct的区别等

如果你感觉理解此节内容还有些吃力,那可能是c++基础没不是很好,那么可以看看C++基本入门理解 --- c语言过渡-CSDN博客 相信一定会有收获,并能理解本节内容

类的定义

类定义格式

  1. class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省略。
  2. 类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或者成员函数;
  3. 为了区分成员变量,会按照惯例加一些特殊标志,如_ 和 m 等。
  4. 定义在类⾯的成员函数默认为inline 内联函数。
解释1,2,3

c++的struct

C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是
struct中可以定义函数。
第一个方面
// C++升级struct升级成了类
// struct名称就可以代表类型
// C++兼容C中struct的⽤法
typedef struct ListNodeC
{
	struct ListNodeC* next;
	int val;
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表类型
struct ListNodeCPP
{
	ListNodeCPP* next;
	int val;
};

这两个函数是一样的,相比c语言来说,更加方便。

第二方面

用class 和 struct 定义类,都是可以的,基本上是一摸一样的;唯一的区别就是:

  • class的类中,定义成员没有被访问限定符修饰时默认为private。
  • struct的类中,定义成员没有被访问限定符修饰时默认为public。

到这里可以会不知道什么是限定符,现在就会清楚简绍!!

访问限定符

基本简绍

C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法(成员函数)结合在⼀块,让对象更加完善,通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤。
public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,,目前来说protected和private是⼀样的,以后继承章节才能体现出他们的区别。
访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有
访问限定符,作⽤域就到 }即类结束。
在上一文章也讲了各种域的关系,这里就重点简绍一下类域!!!

类域

基本内容

类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤ :: 作
⽤域操作符指明成员属于哪个类域。这里与命名空间域的使用方法是类似的;
当声明与定义分离时,需要指定类域; 以下面的代码为例 Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。反之 指定类域Stack,就是知 道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
class Stack {
public:
	void Init(int n = 4);
private:
	int* a;
	int _capacity;
	int _top;
};

void Stack::Init(int n = 4)
{
	int* _a = (int*)malloc(sizeof(int) * 4);
	if (_a == nullptr)
	{
		perror("malloc");
	}
	_capacity = n;
	_top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}

在完成上面的思考后,我们可以在思考一个问题,那就是在 经历  Stack st;   后 ,类域是否开辟了空间呢? 

答案是没有;接下来就来学习一下为什么没有;

实例化

实例化概念

⽤类,类型在物理内存中创建对象的过程,称为类实例化出对象。
类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只
是声明,没有分配空间,⽤类实例化出对象时,才会分配空间
关系如图图纸与房子一样,类就是图纸不能让人居住而类实例化后就是房子能让人居住。
        
class Stack {
public:
	void Init(int n = 4)
	{
		int* _a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
		{
			perror("malloc");
		}
		_capacity = n;
		_top = 0;
	}
	//这里只是声明没有开空间
private:
	int* a;
	int _capacity;
	int _top;
};

int main()
{
	//这里实例化出了对象实体st1和st2;
	Stack st1;
	Stack st2;
	return 0;
}

对象⼤⼩

基本内容

类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?

首先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。
那么对象中是否有存储指针的必要呢?
我们可以知道Strack 实例化st1和st2两个对象,st1和st2都有各⾃独⽴的成员变量 , 存储各⾃的数据,但是st1和st2的成员函数Init指针却是⼀样的,若存储在对象 中就浪费了。
如果Stack实例化100次,函数指针就重复储存100次;因此函数指针是不需要存储的,函数指针的地址是储存在一个公共代码区。
函数指针是⼀个地址,调⽤函数被编译成汇编指 令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在 运⾏时找,就需要存储函数地址,这个我也不太了解,自己可以多搜寻一下。
在编译阶段,这个函数的地址就会储存到一个公共代码区域,每当要使用这个函数时,就会call 这个函数的地址,找到函数 并使用;

内存对⻬规则

  • 第⼀个成员在与结构体偏移量为0的地址处。
  • 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
  • 注意:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
  • VS中默认的对⻬数为8
  • 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩
  • 就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍
这与结构体对齐规则基本一样;但要注意这里对象大小指包括 成员变量没有成员函数。
那么你会有这个疑问:要是没有成员变量只有成员函数呢?
这种情况类的对象大小就为1字节;这并不代表成员函数大小为1字节,而是 这⾥给1字节,纯粹是为了占标识 对象存在。
以下代码
class Date
{
public:
    //原型void Init(Date* const this,int year, int month, int day);
	void Init(int year, int month, int day)
	{
		//也可写为this->_year = year;
		_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2;
    //原型d1.Init(d1,2024, 3, 31);
	d1.Init(2024, 3, 31);
	d1.Print();
	d2.Init(2024, 7, 5);
	d2.Print();
	return 0;
}

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;
还要注意: C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显 ⽰使⽤this指针。

相关问题

观察下面代码,下⾯程序编译运⾏结果是()
A、编译报错 B、运⾏崩溃 C、正常运⾏
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

答案是正常运行; 相信很多人 被 p = nullptr 给坑了;虽然说p是空,但这也只能说明成员变量不存在(类的对象是成员变量),而此代码只是使用了类中的一个函数;根据上面类对象大小中的函数指针可知,函数地址并不存在于类中,使用时也不调用类,因此可以正常使用;

下⾯程序编译运⾏结果是()
A、编译报错 B、运⾏崩溃 C、正常运⾏
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
        cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

这题则是运行崩溃,毕竟调用了成员变量(类的对象)。

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值