当前位置: 首页 > article >正文

C++的类和对象(一)

目录

1、面向过程和面向对象初认识

2、为什么要有类 

3、类的定义 

类的两种定义方式 

4、类的访问限定符

5、类的作用域

5.1 为什么要有作用域?

5.2类作用域

6、类的实例化

6.1类的实例化的定义 

 6.2类的实例化的实现

 6.3经典面试题

7、类对象

7.1类对象的大小计算

 7.2类对象的存储方式猜测

8、this指针 

8.1this指针的介绍 

8.2this指针的特性 


1、面向过程和面向对象初认识

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
就拿外卖来举例:

面向过程关注的就是:客户下单、店家接单、骑手送餐,这三个的过程

面向对象关注的就是:店家、骑手、客户,这三者之间的关系

注意:C++是基于面向对象,不是纯面向对象,也可以面向过程

2、为什么要有类 

在C语言中已经有结构体了,为什么还要设计一个类出来呢?

在C语言中结构体只能用来定义变量,C++中对结构体进行了延伸,不仅可以用来定义变量还可以用来定义函数,而C++更喜欢用class来代替struct,也就是我们经常说的类,并且C++中类的功能可比C语言的结构体强大太多了。面向对象的三大特性:封装、继承、多态,都需要用到类来实现。

3、类的定义 

class Student
{
public:
	void PrintInfo()//成员函数
	{
		cout << name << " " << sex << " " << age << endl;
	}

	char name[10];//成员变量
	char sex[3];
	int age;
};//和结构体一样最后也是要带上分号的

class为定义类的关键字;Student为类名;public为类的访问限定符,一共有三个,后面会讲到;{}里面为类的主体。

类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。 

类的两种定义方式 

1、声明和定义全部放到类体中,注意:如果成员函数在类中定义,可能会被编译器当成内联来处理

2、声明放到.h文件中,定义放到.cpp文件中,实现声明和定义的分离

我们一般在做项目的时候都比较建议采用第二种。

4、类的访问限定符

  1. public修饰的成员在类外可以直接被访问。
  2. protected和private修饰的成员在类外不能直接被访问。
  3. 访问权限作用范围从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果没有下一个访问限定符的出现,那么就一直到 } 类结束。
  4. class的默认访问权限为private。

 注意访问限定符只在编译时有用,当数据保存到内存后,没有任何访问限定符的区别。

经典面试题

C++中struct和class的区别是什么?

C++是兼容C的,所以C++中的struct也可以当成结构体用,还可以用来定义类来使用,和class是一样的,但是struct定义的类的默认访问权限是public,class的默认访问权限是private

5、类的作用域

5.1 为什么要有作用域?

作用域是为了避免变量名冲突和提供代码的组织结构而存在的。作用域定义了一个变量或函数在程序中的可见范围。例如,如果有两个变量具有相同的名称,但它们位于不同的作用域内,它们就不会相互干扰。作用域可以分为全局作用域和局部作用域,全局作用域表示变量或函数在程序中的任何位置都可以访问,而局部作用域表示变量或函数仅在限定的代码块或函数内可见。通过使用作用域,程序员可以更好地控制代码,并减少错误和混淆。

5.2类作用域

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

6、类的实例化

6.1类的实例化的定义 

在C++中,类的实例化指的是使用类定义创建类的一个实例(也称为对象)。当定义一个类时,只是定义了类的结构和属性,但是没有实际的实例存在。为了使用类,必须先创建一个实例,然后可以使用该实例调用类中的方法和访问其属性。

 6.2类的实例化的实现

class Student
{
public:
	void PrintInfo();
private:
	char name[10];        
	char sex[3];
	int age;
};

int main()
{
	Student s;//实例化出一个对象
	s.PrintInfo();//用实例化出的对象访问类中的成员
	return 0;
}

 实例化出的对象只能访问public修饰的成员,protected和private修饰的就访问不了。

 和C语言的结构体一样也是用.进行访问。

一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。

 6.3经典面试题

 了解完类的实例化后,看一下下面的题你是否能做出来

int age;//声明 or 定义?

class Student
{
public:
	void PrintInfo();

	char name[10];
	char sex[3];
	int age;//声明 or 定义?
};

 上面两个age一个在全局中,一个在类中,且都是在.h文件中,他们是声明还是定义?

当你编译的时候很可能会出现这样链接错误。

 先要明白的概念

  1. 声明和定义的区别就在于开没开空间。
  2. 类在没有实例化之前是不占用空间的,只有实例化出对象了才占用空间

打个比方:类实例化出对象就像现实中使用建筑设计图盖房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象(房子)才能实际存储数据,占用物理空间

解答:

  • 全局的age是定义, 类中的age是声明。
  • 链接冲突是因为你是在.h中定义的,.h文件内容最终会在.cpp文件中展开,全局age是定义,当有多个.cpp文件同时编译完后在合成一个目标文件时,多个.cpp文件中都定义了同一个age就会造成冲突

要想解决这个问题可以在全局的age前面加上extern或者static

  • extern是用于声明在另一个文件或模块中定义的变量或函数的关键字。当变量或函数声明为extern时,它告诉编译器该符号在其他地方定义,应该在链接期间解析。这允许函数和变量在程序中的多个文件或模块之间共享。
  • 加static只在当前文件可见,链接属性不一样

7、类对象

7.1类对象的大小计算

// 类中既有成员变量,又有成员函数
class A1 {
public:
	void f1() {}
private:
	char c;
	int a;
};
//类中仅有成员变量
class A2
{
private:
	int a;
};
// 类中仅有成员函数
class A3 {
public:
	void f2() {}
};
//空类
class A4
{};

用sizeof计算他们的大小结果为sizeof(A1):8, sizeof(A2):4,sizeof(A3):1,sizeof(A4):1。

从结果可以看出类的大小的计算也是要遵守结构体内存对齐规则。并且成员函数是不占大小的,空类也是给了1字节的

空类给这1字节的目的是为了占位,占位不存储实际数据,是为了表识对象的存在。

 7.2类对象的存储方式猜测

1. 对象中包含类的各个成员

每个对象中都包含类的各个成员,但是这样缺点很明显,每个对象中成员变量是不同的,但是调用同一个函数,如果按照这种方式存储,当一个类创建多个对象时,每个对象中都会保存一份相同的代码,浪费空间。

2.代码只保存一份,在对象中保存存放代码的地址

 这个方式在多态下会被采用

3.只保存成员变量,成员函数存放在公共的代码段

 这种方式在编译连接时就会根据函数名去公共代码区找到函数的地址,call 函数的地址

 

8、this指针 

8.1this指针的介绍 

this指针是指向当前对象的指针。在C++中,每个对象都有一个指向自身的this指针,这样当有多个对象调用成员函数时就可以做到区分作用。

当在类的成员函数内部访问“非静态的”对象的成员变量或成员时,C++编译器会自动将该对象的地址赋给this指针,以便在函数内部可以通过this指针来访问对象的成员,这些操作对用户是隐藏的,即用户不需要来传递,编译器自动完成。

例如:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
		cout << this->_year << " " << this->_month << " " << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2;
	d1.Init(2023, 11, 10);
	d2.Init(2023, 11, 11);
	d1.Print();
	d2.Print();
	return 0;
}

在上述示例中,Init()和Print()两个成员函数内部都使用了this指针来访问成员变量,在传参的时候编译器隐藏了this指针,不需要用户手动去传的

void Init(Date * const this, int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
d1.Init(&d1, 2023, 11, 10);

 上面的示例是一个伪代码。

实参和形参位置不能显示传递和接收this指针,但是可以在成员函数内部使用this指针。 

8.2this指针的特性 

  • this指针的类型是*const的,const出现在星号右边,表示指针本身是常量,不能给this指针赋值即const pointer,non-const data。
  • 只能在成员函数的内部使用。
  • this指针本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。但是对象中不存储this指针。
  • this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
  • this指针是存放在栈上的,因为它是个形参;但是不同的编译器可能会做优化,像VS编译器就是存放在寄存器上的,速度更快。
  • this指针可以为空。例如下面例子就是可以正常运行的
class Date
{
public:
	void Print()
	{
		cout << this << endl;
		cout << "hello Date" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date* d1 = nullptr;
	d1->Print();//正常运行
	return 0;
}

运行结果

因为它没有对this指针进行使用操作,所以它是正常运行的。

但是下面这个代码就是运行崩溃

class Date
{
public:
	void Print()
	{
		cout << _year << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date* d1 = nullptr;
	d1->Print();//运行崩溃
	return 0;
}

this指针为空,使用了一个空指针去访问成员变量,很明显是空指针问题,所以会运行崩溃。

这两份代码,一个对this指针进行了使用,一个没有,所以结果也会不同。 


如果有写的不好或不对的地方欢迎大家指正。谢谢!!!
 


http://www.kler.cn/a/153981.html

相关文章:

  • 区块链中的wasm合约是什么?
  • JavaScript 原型
  • 什么是循环神经网络
  • Linux的桌面
  • QT仿QQ聊天项目,第三节,实现聊天界面
  • Go语言24小时极速学习教程(二)复合数据(集合)操作
  • 分享88个节日PPT,总有一款适合您
  • 【slab/0x40 UAF】TPCTF2023 - core 一题多解
  • 微信小程序实现打分效果代码整理
  • Golang分布式事务
  • 尝试修改vim光标的思路
  • 文件搜索工具HoudahSpot mac中文版特点
  • 网站更换IP的四大注意事项
  • MagicPipe3D地下管网三维建模数据规格
  • 医疗器械设备模组的具体应用
  • UniApp项目中 使用微信小程序原生语言 进行开发
  • 如何在vs2017及以前版本(vs2010、vs2015)上添加 添加类型库中的MFC类
  • C++作业4
  • 解决了布局问题1和布局问题2,接下来,你的Main函数如果写成下面这样,直接运行,什么也不会显示?
  • java设计模式学习之【建造者模式】
  • TCP网络常见名词
  • 本地navicate连接不上远程mysql主机
  • 基于若依的ruoyi-nbcio流程管理系统增加流程节点配置(三)
  • 【Java Web学习笔记】 2 - CSS入门
  • vue 根据动态生成的form表单的整体的数据回显,赋值及校验和提交
  • 通过转速计算物体的转动弧度(梯形积分法简单应用)