首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >类和对象(上)

类和对象(上)

作者头像
我不是呆头
发布2025-12-20 10:19:08
发布2025-12-20 10:19:08
00
举报

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

面向过程:是一种以过程为中心的编程思想。都是以什么正在发生为主要目标进行编程。C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 面向对象:是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。C++语言是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

例1

二、类的引入

在C语言中,结构体中只能定义变量,但在C++中,结构体内不仅可以定义变量,还可以定义函数。

代码语言:javascript
复制
struct Test
{
	//成员变量
	int a;
	double b;
	//成员函数
	int Add(int x, int y)
	{
		return x + y;
	}
};

但上面结构体的定义,在C++中更喜欢用class来代替。

三、类的定义

  • class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。
代码语言:javascript
复制
class className
{
	//类体:由成员变量和成员函数组成

};  //注意后面的分号
  • 类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。 类的两种定义方式: 1、声明和定义全部放在类体中。需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

2、声明放在头文件(.h)中,定义放在源文件(.cpp)中

注意: 一般情况下,更期望采用第二种方式。

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

1、类的访问限定符

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

【访问限定符说明】  1、public修饰的成员可以在类外直接被访问,在类中可以直接访问。  2、protected和private修饰的成员在类外不能直接被访问。  3、访问权限从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。  4、class的默认访问权限为private,struct为public(因为struct要兼容C)。 注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别 面试题: C++中的struct和class的区别是什么? C++需要兼容C语言,所以C++中的struct可以当成结构体去使用。此外,C++中的struct还可以用来定义类,和class定义类是一样的,区别是struct的成员默认访问权限是public,而class的成员默认访问权限是private

2、类的封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

封装本质上是一种管理:想想我们是如何管理陕西省的兵马俑的。我们若什么都不管,兵马俑就被随意破坏了。所以我们建立了一座房子将兵马俑封装起来。但是我们封装的目的不是为了不给别人看,所以我们开放了售票通道,人们可以通过买票突破封装,在合理的监管机制下进去参观。

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

类也是一样,我们使用类将数据和方法都封装起来。不想对外开放的就用 protected/private 封装起来,用 public 封装的成员允许外界对其进行合理的访问。所以封装本质上是一种管理

五、类的作用域

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

代码语言:javascript
复制
class Person
{
public:
	//显示基本信息
	void ShowInfo();
private:
	char* _name;  //姓名
	char* _sex;   //性别
	int _age;     //年龄
};

//这里需要指定ShowInfo是属于Person这个类域
void Person::ShowInfo()
{
	cout << _name << "-" << _sex << "-" << _age << endl;
}

六、类的实例化

  1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
  2. 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
  3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS   1
#include<iostream>
using namespace std;
typedef int STDateType;
class Stack
{
public:
	void StackInit(int initSize = 4);
	void StackPush(STDateType x);
private:
	STDateType* a;
	int size;
	int capacity;
};
class  A1
{

};
class A2
{
	void fun()
	{

	}
};

int main()
{
	Stack st; //类的实例化,创建一个对象st
	cout << sizeof(st) << std::endl;
	cout << sizeof(A1) << std::endl;
	cout << sizeof(A2) << std::endl;
	return 0;
}

七、类对象模型

1、如何计算类对象的大小

一个类当中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?类的大小又是如何计算的呢?

代码语言:javascript
复制
class Person
{
public:
	//显示基本信息
	void ShowInfo()
	{
		cout << _name << "-" << _sex << "-" << _age << endl;
	}
public:
	char* _name;  //姓名
	char* _sex;   //性别
	int _age;     //年龄
};

2、类对象储存方式猜测

1)、对象中包含类的各个成员
在这里插入图片描述
在这里插入图片描述
2)、只保存成员变量,成员函数存放在公共的代码段
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对于上述两种存储方式,计算机是按照哪种方式来存储的,我们可以通过对下面的不同对象分别获取大小来进行分析:

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

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

3、结构体内存对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。

面试题

结构体怎么对齐? 为什么要进行内存对齐? 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬 件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常 硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。

如何让结构体按照指定的对齐参数进行对齐? #pragma pack(n)//强制使对齐模数为n(不管VS和Linux)

什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景? 大端存储模式:就是内存的低地址上存着数据的高位,高地址上存着数据的低位。 小端存储模式:就是内存的低地址上存数据的低位,而高地址上存数据的高位。 大小端场景:代码移植和网络通信。 结构体内存对齐详解——》请点击

this指针

1、this指针的引出

我们先来定义一个日期类的Date:

代码语言:javascript
复制
#include <iostream>
using namespace std;
class Date
{
public:
	void Display()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	void SetDate(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
	Date d1, d2;//实例化两个日期类
	d1.SetDate(2021, 5, 25);//设置d1的日期
	d2.SetDate(2021, 5, 26);//设置d2的日期
	d1.Display();//打印d1的日期
	d2.Display();//打印d2的日期
	return 0;
}

上述Date类中有SetDate和Display两个成员函数,函数体中并没有关于不同对象的区分,那么当d1调用SetDate函数时,该函数是如何知道要设置的是d1对象,而不是d2对象呢?

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

上述代码调用成员函数传参时,看似只传入了一些基本数据,实际上还传入了指向该对象的指针:

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

编译器进行编译时,看到的成员函数实际上也和我们所看到的不一样,每个成员函数的第一个形参实际上是一个隐含的this指针,该指针用于接收调用函数的对象的地址,用this指针就可以很好地访问到该对象中的成员变量:

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

2、this指针的特性

  1. this指针的类型:类类型* const。
  2. this指针只能在“成员函数”的内部使用。
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
代码语言:javascript
复制
#include <iostream>
using namespace std;
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
	void Show()
	{
		cout << "Show()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;  
	//p->Show();       //第一句代码
	//p->PrintA();     //第二句代码
}

程序分别运行第一句代码和第二句代码,你认为程序运行的结果如何?  你可能看到指针p是一个空指针,而第二句代码和第三句代码都通过操作符“->”,间接性的执行了对p的解引用操作,所以你认为程序会崩溃。  其实不然,当程序执行第一句代码时,程序不会崩溃,会正常打印出字符串"Show()",而当程序执行第二句代码时,程序才会因为内存的非法访问而崩溃。

解释如下:  指针p确实是一个类的空指针,但当执行第一句代码时,程序并不会崩溃。第一句代码并没有对空指针p进行解引用,因为Show等成员函数地址并没有存到对象里面,成员函数的地址是存在公共代码段的。  当程序执行第二句代码时,会因为内存的非法访问而崩溃。执行第二句代码时,调用了成员函数PrintA,这里并不会产生什么错误(理由同上),但是PrintA函数中打印了成员变量_a,成员变量_a只有通过对this指针进行解引用才能访问到,而this指针此时接收的是nullptr,对空进行解引用必然会导致程序的崩溃。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、面向过程和面向对象的初步认识
    • 例1
  • 二、类的引入
  • 三、类的定义
  • 四、 类的访问限定符和类的封装
    • 1、类的访问限定符
    • 2、类的封装
  • 五、类的作用域
  • 六、类的实例化
  • 七、类对象模型
    • 1、如何计算类对象的大小
    • 2、类对象储存方式猜测
      • 1)、对象中包含类的各个成员
      • 2)、只保存成员变量,成员函数存放在公共的代码段
    • 3、结构体内存对齐规则
  • this指针
    • 1、this指针的引出
    • 2、this指针的特性
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档