【C++】类和对象详解(类的使用,this指针)

文章目录

  • 前言
  • 面向过程和面向对象的初步认识
  • 类的引入
  • 类的定义
  • 类的访问限定符和封装性
    • 访问限定符
    • 封装性
  • 类的作用域
  • 类的实例化
  • 类对象模型
    • 如何计算类对象的大小
    • 类对象的存储方式猜测
    • 结构体内存对齐规则
  • this指针
    • this指针的引出
    • this指针的特性
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

在计算机编程领域,程序设计的方法论不断演化,从最初的面向过程到如今更为强大而灵活的面向对象。本文将深入探讨C++中关于类和对象的概念,为读者提供对面向对象编程的深刻理解。

提示:以下是本篇文章正文内容,下面案例可供参考

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

在计算机编程的世界中,面向过程和面向对象是两种不同的编程范式,它们反映了程序设计的不同思想和方式。以下是对这两种范式的初步认识:

面向过程:

面向过程编程是一种以过程为中心的编程思想。在这种范式中,程序被看作一系列的函数或过程的集合,这些函数按照一定的次序依次执行,完成特定的任务。程序的执行流程主要由函数的调用关系来控制。

主要特点包括:

  • 程序的控制流程是线性的,从上到下逐行执行。
  • 重点在于解决问题的步骤和流程,以完成特定的任务。
  • 数据和函数分离,强调过程的执行。

面向对象:

面向对象编程是一种以对象为中心的编程思想。在这种范式中,程序被组织成一组对象,每个对象包含数据和相关的操作,通过这些对象之间的交互来完成任务。

主要特点包括:

  • 程序的控制流程不再是线性的,而是由对象之间的消息传递和方法调用构成。
  • 重点在于组织和管理对象,强调数据的封装和对象的行为。
  • 对象可以被看作是现实世界中的实体,有着状态和行为。

对比和选择:

面向过程和面向对象各有优缺点,选择合适的范式取决于问题的性质和解决方案的需求。面向过程适用于简单的、线性的问题,而面向对象更适用于复杂的、模块化的系统设计。

在实际开发中,很多编程语言提供了同时支持面向过程和面向对象的特性,如C++、Java等,使得程序员可以根据具体情况选择合适的编程方式。这也说明了在面向对象编程的潮流中,面向过程并没有被淘汰,而是与面向对象共同存在,相互补充,为编程提供了更灵活的选择。

类的引入

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

#include <iostream>
#include <cstring>  // 添加头文件以使用字符串库函数
using namespace std;

struct stu
{
    void getName()
    {
        cout << name << endl;
    }
    int age;
    int num;
    char name[20];
};

int main()
{
    struct stu s1;
    strcpy(s1.name, "dzj");  // 使用strcpy将字符串赋值给字符数组
    s1.getName();
    return 0;
}

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

注意:C语言结构体中不能定义成员函数,C++兼容C的语法,并进行拓展,可以定义成员函数

类的定义

类名:由成员函数和成员变量组成,同时注意分号

class className
{
	//类名:由成员函数和成员变量组成
}

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

  • 声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class person
{
public:
    void print()
    {
        cout << _age << _name << _weight << endl;
    }
private:
    int _age;
    char _name[10];
    int _weight;
};
  • 声明放在.h中,类的定义放在.cpp中

在这里插入图片描述
一般情况下,一般采用第二种定义和声明分离的方式,但是为了方便,在平时的练习时,可以采用第一种方式(声明和定义全部放在类体中)

补充:声明和定义的区别是什么?
声明是一种承诺,承诺要干什么,但是还没做,定义就是把这个事情执行了或者落实了

类的访问限定符和封装性

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符

  1. public(公有)
  2. protected(保护)
  3. private(私有)

【访问限定符说明】

  • public修饰的成员在类外可以直接被访问
  • protected修饰的成员类的内部和派生类的成员可以访问,但在类的外部无法直接访问.
  • private修饰的成员只能在本类中访问,在类外不能直接访问
  • 访问权限的作用域从该访问限定符出现的位置直到下一个访问限定符出现为止
  • class的默认访问权限是private,但是struct是public(因为struct要兼容c语言)

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

C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类。
和class是定义类是一样的,区别是struct的成员默认访问方式是public,class的成员默认访问方式是private。

封装性

面向对象的三大特性:封装、继承、多态。
在类和对象阶段,我们只研究类的封装特性,那什么是封装呢?
封装是面向对象编程(OOP)中的一个重要概念,它指的是将数据(变量)和操作数据的方法(函数)捆绑在一起的一种机制。封装有助于隐藏对象的内部实现细节,同时提供一组公共接口供外部使用。这样,对象的内部状态和行为对外部来说是不可见的,只有通过公共接口才能与对象进行交互。

封装的主要目的有以下几点:

  1. 数据隐藏: 将对象的实现细节隐藏起来,防止外部直接访问对象的内部数据,从而提高数据的安全性。

  2. 实现细节隔离: 允许对象实现者修改对象的内部实现细节而不影响外部使用者的代码。外部代码只需要关心对象的接口,而不必关心其具体实现。

  3. 代码组织: 将数据和操作数据的方法封装在一个单元中,使得代码更加模块化、可维护性更强。

在面向对象的语言中,封装通过类来实现。类封装了数据成员和成员函数,成员函数提供了访问和操作数据的接口。访问控制修饰符(如public、private、protected)用于控制成员的可见性和访问权限,实现封装的关键。

示例:

#include <iostream>

class Car {
private:
    // 私有数据成员
    int speed;

public:
    // 公有成员函数
    void setSpeed(int s) {
        if (s >= 0) {
            speed = s;
        }
    }

    int getSpeed() const {
        return speed;
    }
};

int main() {
    Car myCar;

    // 使用公有接口设置速度
    myCar.setSpeed(60);

    // 使用公有接口获取速度
    std::cout << "Current Speed: " << myCar.getSpeed() << " km/h" << std::endl;

    return 0;
}

在这个例子中,Car 类封装了一个私有的速度成员,并提供了公有的 setSpeedgetSpeed 方法来设置和获取速度。外部代码通过这些公有接口与 Car 对象进行交互,而不需要了解具体的实现细节。这就体现了封装的思想。

类的作用域

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

class person
{
public:
    void print();
private:
    int _age;
    char _name[10];
    int _weight;
};
void person::print()
{
    cout << _age << _name<<_weight << endl;
}

类的实例化

类的实例化指的是创建类的对象。在面向对象编程中,类定义了一种数据类型,而实例化则是根据这个数据类型创建具体的对象。对象是类的一个实例,具有类定义的属性和行为。

下面是一个简单的C++示例,演示了如何定义一个类并实例化对象:

#include <iostream>
#include <string>

// 定义一个简单的Person类
class Person {
public:
    // 公有成员函数,用于设置和获取姓名
    void setName(const std::string& n) {
        name = n;
    }

    std::string getName() const {
        return name;
    }

private:
    // 私有数据成员,姓名
    std::string name;
};

int main() {
    // 实例化Person类的对象
    Person person1;

    // 使用公有接口设置姓名
    person1.setName("Alice");

    // 使用公有接口获取姓名
    std::cout << "Person's Name: " << person1.getName() << std::endl;

    // 实例化另一个Person类的对象
    Person person2;

    // 使用公有接口设置姓名
    person2.setName("Bob");

    // 使用公有接口获取姓名
    std::cout << "Person's Name: " << person2.getName() << std::endl;

    return 0;
}

在这个例子中,Person 类定义了一个私有数据成员 name 和公有的成员函数 setNamegetName。通过实例化两个 Person 对象 person1person2,分别设置和获取了它们的姓名。

实例化是面向对象编程中的重要概念,它允许我们根据类的定义创建多个对象,每个对象都拥有自己的状态和行为。

类对象模型

如何计算类对象的大小

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

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

答:对象中只存储成员变量,不存储成员函数

类对象的存储方式猜测

  • 对象中包含类中的各个成员(成员函数和成员变量)
    事实上,这样是不合理的,每个对象中成员变量是不同的,但是调用的却是同一个函数,如果按照这种方式存储,当一个类创建多个对象的时候,每个对象中都会保存一份代码,相同的代码保存了多次,浪费空间。
  • 只保存成员变量,成员函数保存在公共的代码段
    图解:
    在这里插入图片描述
    显然计算机采用的是第二种存储方式(类中对象仅仅存储成员变量)

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

结构体内存对齐规则

关于内存对齐可以看这篇博客

this指针

this指针的引出

#include <iostream>
using namespace std;
class Date
{
public:
	void setDate(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void getInfo()
	{
		cout << _year << endl;
		cout << _month << endl;
		cout << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2;
	d1.setDate(2024, 1, 6);
	d1.getInfo();
	return 0;
}

问题:在上述代码中,有两个成员函数setDate和getInfo,函数体中没有关于类的划分,那么程序是怎么知道d1.setDate(2024, 1, 6)设置的就是d1的空间,而不是d2的空间

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

图解
在这里插入图片描述
这里编译器偷偷帮我们做了处理,但是我们自己并不知道,即在成员函数第一个参数添加 类名*this,并在函数调用的时候传递了对象的地址。

#include <iostream>
using namespace std;
class Date
{
public:
	void setDate(int year,int month,int day)//隐含的this指针Date* this
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void getInfo()
	{
		cout << _year << endl;
		cout << _month << endl;
		cout << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2;
	d1.setDate(2024, 1, 6);//隐含的传递对象地址 d1.setDate(&d1,2024, 1, 6)
	d1.getInfo();
	return 0;
}

this指针的特性

  1. this指针的类型:类名*const
  2. this指针只能在“成员函数”的内部使用
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数的时候,将对象地址作为实参传递给this形参,所以对象中不存储this指针
  4. this指针是成员函数第一个隐含的指针形参,(vs2022下)由编译器通过ecx寄存器自动传递,不需要用户传递

this指针存储在哪里?

上面说到this指针本质是成员函数的第一个形参,那么肯定存放在进程地址空间中的栈区,但是在vs2022下是存放在ecx寄存器中,不同编译器和环境下可能会有差异

this指针可以为空吗?

通过一段代码来说明

#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->PrintA();
	p->Show();
}

上面代码中,p->PrintA()会发生程序崩溃,但是p->Show()可以正常运行。因为在PrintA()中发生了隐含的this指针的解引用操作(对空指针的解引用),但是在Show()并没有解引用所以程序没问题

总结

通过本文的学习,我们深入了解了C++中类和对象的方方面面,从面向过程的初步认识到类的引入和定义,再到类的作用域、实例化、访问限定符及封装,以及类对象大小的计算和类成员函数的this指针。这些概念和技术不仅为我们提供了更灵活的程序设计手段,也为构建更为可维护和可扩展的软件系统奠定了基础。在今后的编程学习和实践中,希望读者能够充分运用这些知识,编写出更高效、健壮的C++程序。面向对象编程不仅仅是一种技术,更是一种思想,期待读者通过不断实践,能够更深刻地理解和运用面向对象的优秀编程范式。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

红帽Redhat安装教程及安装出错(Liunx)

一、红帽5安装 1.打开vmware,新建虚拟机。或者文件→新建虚拟机 2.自定义,下一步 3.下一步 4.稍后安装操作系统,下一步 5.linux 红帽5 64位,下一步 6.给虚拟机取名字,选择安装路径。下一步 7.下一步(可以根据自己的电脑配置稍微增加数量) 8.4GB 下一步 9.仅主机(根据需…

运维工程师——敢问路在何方!

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

[NISACTF 2022]popchains

[NISACTF 2022]popchains wp 题目代码&#xff1a; Happy New Year~ MAKE A WISH <?phpecho Happy New Year~ MAKE A WISH<br>;if(isset($_GET[wish])){unserialize($_GET[wish]); } else{$anew Road_is_Long;highlight_file(__FILE__); } /**********************…

RTT打印时间戳

官方的RTT VIEWER没有打印接收时间戳的功能&#xff0c;经过查找后发现可以有以下三种打印时间戳的方法。 第三方的RTT上位机ExtraPutty自己打印 第三方的RTT上位机 码云上有一个RTT_T2的仓库&#xff0c;基于python qt包写的画面&#xff0c;通过pylink来jlink通信。 优点…

CorelDRAW 2023 中文破解、终身永久版 (附序列号)

CorelDRAW2023是一款非常专业的电脑图像设计工具。该产品推出了全新的2023版本&#xff0c;在功能和体验上更进一步&#xff0c;最新的填充和透明设备功能可以完全控制任何类型的纹理&#xff0c;适用于网络摄影、印刷项目、艺术、排版等&#xff0c;让你可以更好的进行图像设计…

安全加密基础—基本概念、keytool、openssl

安全加密基础—基本概念、keytool、openssl 目录 前言 一、概念 明文通信 无密钥密文通信 对称加密 非对称加密 数字签名 消息摘要(MD5) CA数字证书(解决公钥分发的问题) HTTPS 相关文件扩展名 常用后缀名 普通的pem文件内容 二、keytool 2.1常用的命令如下 2…

网络优化篇(一)---------TCP重传性能优化

本文通过一个TCP重传优化的实际问题,详细讲解问题的分析、定位、优化过程。 通过本文你将学到: 如何通过linux命令和/proc文件系统分析TCP性能数据如何通过linux命令和netlink api分析某个具体的TCP连接的性能数据如何通过bcc工具分析TCP性能数据如何通过调整系统参数优化TCP重…

第1章 线性回归

一、基本概念 1、线性模型 2、线性模型可以看成&#xff1a;单层的神经网络 输入维度&#xff1a;d 输出维度&#xff1a;1 每个箭头代表权重 一个输入层&#xff0c;一个输出层 单层神经网络&#xff1a;带权重的层为1&#xff08;将权重和输入层放在一起&#xff09; 3、…

从零开始C++精讲:第一篇——C++入门

文章目录 前言一、C关键字二、命名空间2.1引子2.2命名空间定义2.3命名空间的使用 三、C输入和输出3.1输出3.2输入 四、缺省参数4.1全缺省4.2半缺省 五、函数重载5.1重载概念 六、引用6.1定义6.2引用的使用示例6.2.1引用作参数6.2.1引用作返回值 6.3传值、传引用效率比较6.4常引…

真空引水罐 虹吸抽水机 负压虹吸罐 农业灌溉工作原理动画介绍

​ 1&#xff1a;真空引水罐虹吸抽水机虹吸罐介绍 真空引水罐是一种水泵吸水设备&#xff0c;也被称为真空罐、吸水罐或自动引水装置。它是一个密封的罐体&#xff0c;被串联在泵前的吸水管上&#xff0c;能够使水泵的吸水口从负压吸水变为正压吸水。使用真空引水罐可以节省真…

彻底解决vue-video-player视频铺满div

需求 最近需要接入海康视频摄像头&#xff0c;然后把视频的画面接入到自己的网站系统中。以前对接过rtsp固定IP的显示视频&#xff0c;这次的不一样&#xff0c;没有了固定IP。海康的解决办法是&#xff0c;摄像头通过配置服务器到萤石云平台&#xff0c;然后购买企业版账号和…

解决vue3中watch 监听不到旧值的问题,亲测有效!

问题描述 这个问题是我在公司vue3项目的时候发现的一个问题&#xff0c;watch 在监听对象/数组变量的变化时&#xff0c;发现对象的数据变化时 旧数据 获取到的和新数据是一样的 类似于下面这样 const objref({a:我是原来的值,b:6, })obj.a改变值watch(obj,(nel,old)>{ c…

关于曲率、曲率半径和曲率圆,看这几篇文章就够啦

关于曲率、曲率半径和曲率圆的内容&#xff0c;是考研数学数学一和数学二大纲中明确要求掌握的内容&#xff0c;但这部分内容在很多教材教辅以及练习题中较少涉及。在本文中&#xff0c;荒原之梦考研数学网就为大家整理了曲率、曲率半径和曲率圆方程相关的概念、基础知识以及练…

无心剑七绝《译无止境》

七绝译无止境 人生跌宕几春秋 苦辣酸甜永不休 只待通灵成妙译 神思曼舞醉琼楼 2024年1月6日 平水韵十一尤平韵 无心剑的这首《译无止境》以七言绝句的形式&#xff0c;表达了对翻译事业的热爱和追求。 首句“人生跌宕几春秋”&#xff0c;意味着人生的曲折变化&#xff0c…

2024/1/7周报

文章目录 摘要Abstract文献阅读题目引言贡献相关工作Temporal RecommendationSequential Recommendation 方法Problem FormulationInput EmbeddingSelf-Attention StructureModel Training 实验数据集实验过程实验结果 深度学习Self-attention多头机制堆叠多层 总结 摘要 本周…

静态网页设计——宠物狗狗网(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1nk4y1X74M/?vd_source5f425e0074a7f92921f53ab87712357b 使用技术&#xff1a;HTMLCSSJS&#xff08;…

深度学习|4.1 深L层神经网络 4.2 深层网络的正向传播

4.1 深L层神经网络 对于某些问题来说&#xff0c;深层神经网络相对于浅层神经网络解决该问题的效果会较好。所以问题就变成了神经网络层数的设置。 其中 n [ i ] n^{[i]} n[i]表示第i层神经节点的个数&#xff0c; w [ l ] w^{[l]} w[l]代表计算第l层所采用的权重系数&#xff…

【响应式编程-05】Lambda方法引用

一、简要描述 Lambda的方法引用也叫引用方法 方法引用初体验方法引用的底层实现方法引用的语法格式方法引用举例 静态方法引用构造方法引用普通方法引用super和this方法引用数组的方法引用 二、方法引用初体验 为什么出现方法引用&#xff1f; 引用已存在方法&#xff0c;避免重…

数据结构第八弹---队列

队列 1、队列的概念和结构2、队列的实现2.1、头文件包含和结构定义2.2、初始化2.3、销毁2.4、判断是否为空2.5、入队2.6、出队2.7、获取队头数据2.8、获取队尾数据2.9、获取有效数据个数 3、代码汇总总结 1、队列的概念和结构 队列&#xff1a;只允许在一端进行插入数据操作&am…

给定0-1数组,找出连续1最长和次最长的2个子数组的起始位置和结束位置。

题目 给定0-1数组&#xff0c;找出连续1最长和次最长的2个子数组的起始位置和结束位置。 要求&#xff1a; 子数组长度大于等于1。 如果有多个子数组满足条件&#xff0c;按照数组下标由小到大只输出满足条件的前2个数组的起始位置和结束位置&#xff0c; 如果只有1个满足&…