【C++之类和对象】初识类和对象

news2025/1/11 18:36:32

目录

    • 前言
    • 一、面向对象VS面向过程
    • 二、类
    • 三、类的定义
    • 四、类的访问限定符
    • 五、封装
    • 六、C++中的用struct和用class定义的类有何不同?
    • 七、类的作用域
    • 八、类的实例化
    • 九、计算类对象的大小
    • 十、this指针

前言

C++是一门面向对象的语言,之前学习的C语言是一种面向过程的语言,通过学习,我们就需要知道面向过程和面向对象的区别了。本文主要介绍C++中的类和如何使用类来创建出对应的对象,这是学习C++后面更加复杂的内容的基础。

一、面向对象VS面向过程

  • C语言是一门面向过程的语言,也就是再解决问题的过程中更加注意问题的每一个过程要如何进行处理。
  • C++是一门面向对象的语言,也就是再解决问题的过程中更加注意采用各种类来创建出对应的对象来解决问题。
    下面举一个形象的例子来更好地理解面向对象和面向过程:
    在这里插入图片描述
    在这里插入图片描述
    通过上面这两个图可以看出:面向过程更加关注洗衣服中需要做哪些事情,而面向对象更加关注洗衣服过程需要哪些角色。

二、类

C++中的类和C语言中的结构体优点相似,但是又有所差异,C++中的类对C语言中的结构体进行了升级,在C++中,由关键字struct定义出来的东西不再叫做结构体,而是叫做类。
C++中使用struct定义出来的类和C语言中定义的结构体中有什么区别呢?

  1. C语言中使用struct定义的结构体的名字不能直接作为类型,如果想要使用名字作为类型,则需要对struct+名字进行typedef,C++中struct定义出来的类中,类名是可以直接作为类型名的。比如下面这段代码,在C++中是对的,但是在C语言中就会报错。
    在这里插入图片描述
    C语言必须在名字前加上struct关键字。
  2. C语言中struct定义的结构体只能怪定义成员变量,C++中的类既能定义成员变量,又能定义成员函数
    在这里插入图片描述
    在这里插入图片描述
    结果:
    在这里插入图片描述

三、类的定义

在这里插入图片描述

class为定义类的关键字ClassName类的名字{}中为类的主体,注意类定义结束时后面有分号
类中的元素称为类的成员类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数

通过上面的学习,我们知道C语言中使用的struct关键字可以定义结构体,在C++中升级为类,但是在C++中我们更加喜欢使用class关键字来定义类。因此上面的类就可以改成以下 样子:
在这里插入图片描述

在这里插入图片描述
但是此时会发现一个问题,我们在类外调用成员函数的时候发现报错了,刚刚使用struct定义类的时候并没有报错,这是为啥呢??
原因是:使用class来定义类的时候,类中的所有成员默认是私有访问的,使用struct来定义类的时候,类中的成员默认是公有的。所谓的私有,就是指在类外不能访问,只能在类内进行访问。所谓的公有,就是指在类内和类外都能够进行访问。
通常我们在设计类的时候,如果想要给别人在类外进行访问的,我们可以手动设置成公有,如果不想给别人在类外进行访问的,我们可以手动设置成私有,习惯上,我们在设计一个类的时候,通常不想要别人在类外进行访问我们类中的成员变量属性,而是通过我们在类中提供的成员函数间接访问成员变量,因此,我们会将成员变量设置成私有,将成员函数设置成公有。设置成公有时只需要在前面加上访问限定符public,设置成私有就是在前面加上private
因此,解决上面的问题,我们可以考虑这样子做,具体如下:

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

  • 类定义的两种方式
  1. 成员函数声明和定义全部放在类体中,都放在头文件中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理,比如:
class Person
{
public:
	void Init(const char* name,int age)
	{
		strcpy(_name, name);
		_age = age;
	}

	void Print()
	{
		cout << _name << endl;
		cout << _age << endl;
	}
private:
	char _name[10];
	int _age;
};

  1. 声明放在.h文件中,类的定义放在.cpp文件中,此时在.cpp文件中需要在函数名前面指明类域,否则会报错,如上面的代码应该修改为:
  • 头文件中:放成员函数的声明
class Person
{
public:
	void Init(const char* name, int age);

	void Print();
private:
	char _name[10];
	int _age;
};
  • 源文件中:放成员函数的定义
void Person::Init(const char* name, int age)
{
	strcpy(_name, name);
	_age = age;
}

void Person::Print()
{
	cout << _name << endl;
	cout << _age << endl;
}

四、类的访问限定符

在C++中的类中,访问限定符有三个:public,private,protected访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止

  • public:表示公有访问,也就是被public修饰的成员可以在类外和类内进行访问
  • private:表示私有访问,也就是被private修饰的成员只能在类内进行访问,而不能在类外进行访问
  • proteced:目前我们暂时不需要使用,所以目前我们只需要把它当成private即可。具体在继承将会进行介绍。

五、封装

封装是面向对象语言的三大特性之一,封装是指用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。也就是相比于面向过程而言,面向过程中的成员变量和成员函数是分离的,因此不方便对此进行管理,在设计类的时候,我们将类的成员变量和成员函数封装在一起,并且通过类的访问限定符来限定别人对类中的成员进行访问,即允许别人直接在类外访问的成员,我们设置成公有,不允许别人在类外直接访问的成员,我们设置成私有,但是别人可以在类外间接通过类提供的成员函数进行访问。封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

六、C++中的用struct和用class定义的类有何不同?

C++兼容C语言,因此C++中用struct定义的类可以当作结构体进行使用,也可以当成类去使用,使用struct进行定义时,类中的成员默认是公有的,使用class进行定义时,类中的成员默认是私有的

七、类的作用域

我们定义的类也是存在一个作用域的,和局部变量存在一个作用域是一样的道理,类存在的作用域称为类的作用域,简称类域。类域是从类定义的左花括号开始,到类定义的右花括号结束。

在这里插入图片描述

有一些特殊场景,成员函数的声明和定义分离时,在成员函数的定义中,我们需要在成员函数名的前面指明类域,所以这一部分也是属于类域中的一部分,而不是属于类外,但是这种定义是在类外定义的,但是仍然属于这个类。

在这里插入图片描述

八、类的实例化

类相当于是一个模板,实际中是不存在的,通常,我们是利用类来实例化出各种对象或者说创建对象,类只是告诉我们创建出来的对象中存在什么成员,成员变量就是告诉我们这个类的属性,成员函数就是告诉如何去操作这个类,或者可以对这个类进行什么操作。比如:假如有一个日期类,那么我们可以根据这个日期类创建出很多的对象来表示日期。

  • 定义一个日期类
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 和 d2就是根据Date类创建出来的对象
	return 0;
}

九、计算类对象的大小

我们知道,一个类可以创建出很多对象,那么我们应该如何来计算一个类创建出来的对象的大小呢??我们知道,每一个类中可能包含这个类的成员函数和成员变量,那么这个类创建出来的对象是否也会同时存在这个类的成员变量和成员函数呢??显然不是,我们知道,一个类中的成员变量表示的是这个类创建出来的对象特定的属性,那么每一个对象的属性肯定是不一样的,所以每一个对象的成员变量肯定是独有的,因此,每一个对象都应该存在独特的一份类的成员变量来表示自己的属性。对于成员函数而言,通过测试我们可以知道,不同的对象在调用类的成员函数时,如果调用的成员函数名以及参数一样,那么调用的其实是同一个函数,因此,类的成员函数并不需要在类创建出来的每一个对象中都存一份,这样会浪费空间。类的成员函数其实是存在一个公共的代码区每一个对象在调用类的成员成员时,都会去这个公共的代码区找想要调用的成员函数。所以,计算一个类创建出来的对象的大小时,并不需要考虑类中的成员函数,但是需要注意的是结构体中的内存对齐还是需要考虑的。

  • 例子1:
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	char _a;
};

int main()
{
	A a;
	cout << sizeof(a) << endl;

	return 0;
}

结果:
在这里插入图片描述

  • 例子2:类中既有成员变量,又有成员函数
    计算方法:只需要考虑类的成员变量进行内存对齐即可。
// 类中既有成员变量,又有成员函数
class A1 {
public:
	void f1()
	{}
private:
	int _a;
};
int main()
{
	A1 a;
	cout << sizeof(a) << endl;

	return 0;
}

结果:
在这里插入图片描述

  • 例子3:类中仅有成员函数
    计算方法:这种情况下需要一个字节对这个类进行占位,表示这个类的存在
// 类中仅有成员函数
class A2 {
public:
	void f2() {}
};
int main()
{
	A2 a;
	cout << sizeof(a) << endl;

	return 0;
}

结果:
在这里插入图片描述

  • 例子4:类中什么都没有—空类
    计算方法:和例子3类似,这种情况下需要一个字节对这个类进行占位,表示这个类的存在
// 类中什么都没有---空类
class A3
{};
int main()
{
	A3 a;
	cout << sizeof(a) << endl;

	return 0;
}

结果:
在这里插入图片描述

十、this指针

this指针是类中隐藏的一个指针,不会显示写出来,但是实际上是存在的,在类的每一个成员函数中,都会存在一个this指针是指向调用这个成员函数的对象,也就是存放的是调用这个成员函数的对象的地址。通过this指针,我们就知道是哪个对象调用这个成员函数,从而找到这个对象相关的属性,如果没有this指针,通过前面的学习我们知道,类的成员函数是存放在一个公共的代码区的,不是在每一个对象中都存放的,所以不同的对象调用同一个函数名参数相同的函数时调用的就是同一个函数,那么成员函数就无法区分到底是哪个对象调用的,就会出现问题。

平时我们的写法:并没有将this指针显然写出来
在这里插入图片描述
在这里插入图片描述
实际上:编译器会对上述的代码做如下处理
在这里插入图片描述
在这里插入图片描述

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

  • this指针的特性
  1. this指针的类型:类类型* const,即this指针本身不能被改变
  2. 只能在“成员函数”的内部使用,并且只能是非静态成员函数,后面我们会学习类的静态成员函数,这个函数里面是不存在this指针的。
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
  5. this指针是类的非静态成员函数中的一个形参,所以this指针是存在于函数栈帧中的,也就是存在于栈区中。

关于this指针的一些经典题目

  • 代码1
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
	void Show()
	{
		cout << "Show()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
}

运行结果
在这里插入图片描述

分析:上述代码中,p是一个空指针,程序会将p传给成员函数PrintA()中的this指针,然后通过this指针去访问对象中的_a,由于p是一个空指针,上述的操作就是通过一个空指针去访问一个对象中的成员属性,显然就是对一个空指针进行了解引用,所以会出现程序崩溃。

  • 代码2
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
	void Show()
	{
		cout << "Show()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Show();
	return 0;
}

结果:
在这里插入图片描述

分析:上述代码的运行结果显然就是正常结束,虽然p仍然是一个空指针,也通过了函数传参传给了成员函数中的this指针,但是在成员函数中并没有对this指针进行解引用,所以程序不会发生崩溃。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对epoll的重新学习【附源码】

目录 一、概述 二、使用 三、API 3.1 epoll_create(int size) 3.2 epoll_ctl(int epfd,int op, int fd. struct epoll_event *event) 3.3 epoll_wait(int epfd, struct peoll_event *events, int maxevents, int timeout) 3.4 *ssize_t read(int fd, void buf, size_t c…

python模块之codecs

python 模块codecs python对多国语言的处理是支持的很好的&#xff0c;它可以处理现在任意编码的字符&#xff0c;这里深入的研究一下python对多种不同语言的处理。 有一点需要清楚的是&#xff0c;当python要做编码转换的时候&#xff0c;会借助于内部的编码&#xff0c;转换…

Spark读取Hive数据的两种方式与保存数据到HDFS

Spark读取Hive数据的两种方式与保存数据到HDFS Spark读取Hive数据的方式主要有两种 1、 通过访问hive metastore的方式&#xff0c;这种方式通过访问hive的metastore元数据的方式获取表结构信息和该表数据所存放的HDFS路径&#xff0c;这种方式的特点是效率高、数据吞吐量大、…

规则引擎-drools-4-动态生成drl文档

文章目录drools 引擎工作原理动态生成drl文件示例步骤模板文件 decision_rule_template.drt生成规则文件serviceDecisionNodeFact实体对象生成的drl字符串如下KieHealper 执行动态生成drl文件的原理实际应用过程中&#xff0c;很多时候&#xff0c;规则不是一成不变的&#xff…

54.Isaac教程--RealSense相机

RealSense相机 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录RealSense相机RealsenseCamera Codelet示例应用程序故障排除固件注意事项通过 USB 3.0 电缆使用 USB 3.0 端口x86_64 Linux 主机设置设置电源模型英特尔RealSense 435 摄像头…

分享159个ASP源码,总有一款适合您

ASP源码 分享159个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 159个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1EaQuRA6mxyylrNWLq8iKVA?pwdaljz 提取码&#x…

springmvc知识巩固

文章目录回顾spring知识前言什么是SpringMVCSpringMVC的优点SpringMVC的常用注解Controller注解的作用ResponseBody注解的作用SpringMVC重定向和转发SpringMVC主要组件SpringMVC的执行流程回顾spring知识 上篇整理了“spring知识巩固”常见面试题&#xff0c;有需要的伙伴请点…

Java基础:源码讲解Collection及相关实现List、Set、Queue

1 缘起 说到Java第一问&#xff0c;很多人的第一反应是三大特性&#xff0c;那么接下来&#xff0c;可能就是集合了。 Collection是Java必知必会&#xff0c;即使没有系统学习&#xff0c;在实际的开发过程中&#xff0c;Collection也是应用最广泛的。 当然&#xff0c;一般的…

ESP-IDF:归并排序测试

ESP-IDF:归并排序测试 /归并排序测试/ void printarry18 (int arr[],int length) { for(int i0;i<length;i) { cout<<arr[i]<<" "; } cout<<endl; } void merge(int arr[],int start, int end, int mid,int * temp) { int length 0;//记录te…

进程间通信之管道(匿名管道与命名管道)

进程间通信之管道进程间通信管道什么是管道管道分类——1.匿名管道匿名管道举例管道的特点管道分类——2.命名管道创建一个命名管道举例命名管道的打开规则匿名管道与命名管道的区别具体使用举例&#xff1a;例子1-用命名管道实现文件拷贝例子2-用命名管道实现server&clien…

POI介绍简介

2.1 POI介绍 Apache POI是用Java编写的免费开源的跨平台的Java API&#xff0c;Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能&#xff0c;其中使用最多的就是使用POI操作Excel文件。 jxl&#xff1a;专门操作Excel maven坐标&#xff1a; <depend…

Linux中安装MySQL及常见问题汇总

一、安装前工作 1、卸载之前的数据库 在安装前需要确定现在这个系统有没有 mysql&#xff0c;如果有那么必须卸载 &#xff08;在 centos7 自带的是 mariaDb 数据库&#xff0c;所以第一步是卸载数据库&#xff09;。 #查看mariadb数据库&#xff1a; rpm -qa | grep maria…

moment.js根据时间戳计算与当前时间相差多少天

Moment旨在在浏览器和Node.js中工作。 所有代码都应该在这两种环境中都有效&#xff0c;并且所有单元测试都在这两种环境中运行。 目前&#xff0c;以下浏览器用于ci系统&#xff1a;Windows XP上的Chrome&#xff0c;Windows 7上的IE 8,9和10&#xff0c;Windows 10上的IE 1…

小车程序、安装vs2019和必要的相关软件

环境 sqlserver2019 vs2019企业版本 安装vs2019步骤 小车程序 C# .net vs2019 sqlserver2019 小车程序地址 小车运行的几种轨迹 一&#xff0c;自动运行到指定节点 找到manual按钮&#xff0c;找到目的节点&#xff0c;search move。 二&#xff0…

[数学] 三次样条

三次样条 已知有一组点x0,x1,x2,⋯,xnx_0, x_1, x_2, \cdots, x_nx0​,x1​,x2​,⋯,xn​, 其中, xt<xt1x_t<x_{t1}xt​<xt1​, y(xt)yty(x_t)y_ty(xt​)yt​, 及该点处的切线y′(xt)yt′y(x_t)y_ty′(xt​)yt′​ 每两个相邻的点之间可以作一个三次曲线 在所有相邻…

let/const相关内容(二)

let/const作用域提升 1&#xff09;根据前面所学的内容知道&#xff0c;var定义的变量是可以作用域提升的。 console.log(foo); // undefined var foo "foo"虽然在第一行中foo还没有被定义&#xff0c;但是在执行代码前&#xff0c;会预编译&#xff0c;先定义f…

ModStartBlog v6.6.0 多语言增强,缓存后台优化

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场&#xff0c;后台一键快速安装会…

【完美解决】Github action报错remote: Write access to repository not granted.

报错及效果图报错代码效果图解决方案必要步骤可能有效的步骤报错及效果图 本解决方案是笔者通过Github action运行项目时报错的解决方案&#xff0c;如果是本地运行报此错&#xff0c;未必有效果。 报错代码 remote: Write access to repository not granted. fatal: unable t…

密评FAQ:如何确定网络和通信安全层面的测评对象?

信息系统一般通过网络技术来实现与外界的互联互通&#xff0c;GB/T 39786-2021《信息安全技术信息系统密码应用基本要求》规定了信息系统在网络和通信安全层面的密码应用技术要求&#xff0c;这些要求涉及到通信的主体&#xff08;通信双方&#xff09;、信息系统与网络边界外建…

(1)深入理解Java虚拟机-内存模型

深入理解Java虚拟机 Java虚拟机运行时数据区 程序计数器 ​ 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的 字节码的行号指示器。在Java虚拟机的概念模型里 [1] &#xff0c;字节码解释器工作时…