C++类和对象(上)

news2025/2/4 15:53:07

 学习“类”不“类”,有“对象”了吗?

 

目录

面向过程和面向对象

类的引入

类的定义

访问限定符

封装

类的作用域

类的实例化

类对象的存储方式

计算类对象的大小

this指针

 this指针的特性

this指针两问


面向过程和面向对象

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

 以考驾照为例:面向过程在意的是你在驾校练车的每一次练车,考试时的每一个操作步骤,踩离合、松手刹、踩油门.......

C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完
成。

整个拿驾照的过程:报驾校、练车、考试、通过、拿证。
以最后的科目考试为例,总共有3个对象:驾驶员、教练员、汽车。

整个考试过程是这三个对象交互完成的,驾驶员在车上“眼花缭乱”的操作,教练员怎样保护我的安全,车子做出怎样的反映,这些都不需要关心! 

类的引入

如果把学生定义成一个类,你们学校的每个同学都是一个对象。每个学生的姓名,学号,家庭住址都是成员变量。学习、运动、谈恋爱就是成员方法(函数)。

你不屑的瞟了一眼,“就这”!我用C语言结构体就能搞定呀C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。

#include "add.h"
using namespace std;

struct Student
{
	//学习
	void Study()
	{
		//...
	}
	//运动
	void Exercise()
	{
		//...
	}
	//谈恋爱
	void Love()
	{
	   //...
	}
	char* name;
	char* add;
    int number;
};

int main()
{
	Student LiSi;
	LiSi.Study();
	LiSi.Exercise();
	LiSi.Love();
	return 0;
}

在C++中更喜欢用class关键字来代替上面结构体的定义!

类的定义

class为 定义类的关键字,Student为类的名字,{}中为类的主体,注意类定义结束时后面
号不能省略
类体中内容称为类的成员:类中的变量 称为类的属性成员变量; 类中的函数称为类的方法或者
成员函数。
class Student
{
// 类体:由成员函数和成员变量组成
}; 
●声明和定义全部定义在类中
class Student
{
public:
	void Init(int year,int month,int day)
	{
		//...
	}
private:
	int _year;
	int _month;
	int _day;
};

●类声明在.h文件中,定义在.cpp文件中

Student.h

class Student
{
public:
	void Init(int year,int month,int day);
private:
	int _year;
	int _month;
	int _day;
};

Student.cpp

void Student::Init(int year,int month,int day)
{
	//...
}

既然struct可以定义类,c++为什么要引入class关键字呢?

C++ 需要兼容 C 语言,所以 C++ struct 可以当成结构体使用,也 可以用来定义类,和class 定义类是一样的。区别是 struct 定义的类默认访问权限是 public class 定义的类默认访问权限是private。 ( public和private是两个访问限定符)
class Data
{

	void Init(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "年" << _month <<"月" << _day <<"日" << endl;
	}

	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d1;
	d1.Init(2001,10,3);
	d1.print();
  
	d1._year = 2022;
	d1._month = 12;
	d1._day = 20;
	d1.print();
	return 0;
}

class定义的类默认访问权限是private!

 struct 定义的类默认访问权限是public!

struct Data
{

	void Init(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "年" << _month <<"月" << _day <<"日" << endl;
	}

	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d1;
	d1.Init(2001,10,3);
	d1.print();

	d1._year = 2022;
	d1._month = 12;
	d1._day = 20;
	d1.print();
	return 0;
}

访问限定符

● public修饰的成员在类外可以直接被访问。
class Data
{
public:
	void Init(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "年" << _month <<"月" << _day <<"日" << endl;
	}
	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d1;
	d1.Init(2001,10,3);
	d1.print();

	d1._year = 2022;
	d1._month = 12;
	d1._day = 20;
	d1.print();
	return 0;
}

● protected和 private 修饰的成员在类外不能直接被访问 ( 此处 protected private 是类似的 )。
还是上面的代码,用privte修饰成员变量:
class Data
{
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;
};

●访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
●如果后面没有访问限定符,作用域就到“}” 即类结束。
class的默认访问权限为 privatestructpublic(因为struct要兼容C)。

封装

在类和对象阶段,主要是研究类的封装特性!
定义:封装本质上是一种管理,让用户更方便使用类 将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

 

对于汽车的驾驶者而言,不用关心内部核心部件,比如发动机是如何布局的,电机控制器 是如
何设计的等,驾驶员只需要知道,怎么发动汽车、怎么通过油门、方向盘、刹车等与汽车进行交互即可。因此汽车 出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供启动、油门以
及刹车等,让驾驶员可以与汽车进行交互即可!!
C++ 语言中实现封装,可以 通过类将数据以及操作数据的方法进行有机结合,通过访问权限来 隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用

类的作用域

类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
可以这样理解,类域就是我们的总部,Init虽然在总部外为公司生产部件,但是只要冠名总部的名字,就可以知道你是总部的一份子!
class Data
{
public:
	void Init(int year, int month, int day);
private:
	int _year;
	int _month;
	int _day;
};
	//Init是属于Data这个类域
void Data::Init(int year, int month, int day)
{
		_year = year;
		_month = month;
		_day = day;
}

类的实例化

简单来说,就是用类型创建对象(变量)的过程,称为类的实例化。

是对对象进行描述的,是像模型一样的东西,就比方是工匠造家具的图纸,这个图纸只是设计好需要那些材料,建造方法。但是并没有实际建造出来。所以并没有给类分配实际的内存空间!

下面的写法就是错误的,Student就好比是“图纸”。

struct Student
{
	//学习
	void Study()
	{
		//...
	}
	char* name;
	char* add;
	int number;
};

int main()
{
	Student.numner = 20060240;

	return 0;
}

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

class Chair
{
public: 
	void Init(char color,char type,int price)
	{
		_color = color;
		_type = type;
		_price = price;
	}
	void Printf()
	{
		cout << " 椅子颜色:" << _color << 
			    " 椅子型号:" << _type << 
			    " 椅子价格:" << _price << endl;
	}
private:
	char _color;
	char _type;
	int _price;
};

int main()
{

	Chair c1;
	c1.Init('R', 'X', 66);
	c1.Printf();
	Chair c2;
	c2.Init('B','Y',99);
	c2.Printf();
	return 0;
}

类对象的存储方式

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

还是以学生类为例,每个学生都是一个对象,他们的性格,特征确实各不相同。但是,他们都需要吃饭,这个时候如果给每个学生都建造一个食堂,就是一种资源浪费!只需要在一块土地上建一个食堂,用餐的时候,所有同学都过来就好了。

计算类对象的大小

一个类的大小,实际就是该类中” 成员变量”之和,当然要注意内存对齐 。
内存对齐在之前的博客提到过:
自定义类型:结构体、枚举、联合体_Bug程序员小张的博客-CSDN博客结构体https://blog.csdn.net/weixin_59351791/article/details/126901411?spm=1001.2014.3001.5502 计算类对象的大小
情况1:类中既有成员变量,又有成员方法。
class A
{
public:
	void Init()
	{
		//...
	}
private:
	char a1;
	char a2;
	int age;
};

int main()
{
	A a;
	cout <<"类对象的大小为:" << sizeof(a) << endl;
	return 0;
}

 

情况2:类中只有成员方法。
class A
{
public:
	void Init()
	{
		//...
	}
    void Print()
	{
		//...
	}
};

int main()
{
	A a;
	cout << "类对象的大小为:" << sizeof(a) << endl;
	return 0;
}

情况3:空类。
空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象!
class A
{
};

int main()
{
	A a;
	cout << "类对象的大小为:" << sizeof(a) << endl;
	return 0;
}

 

this指针

 在回过头看刚刚的一份代码,Date类中有 Init Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

class Data
{
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()
{
	Data d1;
	d1.Init(2001, 10, 3);
	d1.print();
    Data d2;
	d2.Init(2020, 11, 4);
	d2.print();
	return 0;
}
 C++中引入了 this指针,编译器给每个“ 非静态的成员函数“增加了一个 隐藏的指针参数,让 该指针指向当前对象(函数运行时调用该函数的对象), 在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,用户不需要来传递,编译器自动完成。

 

 this指针的特性

●this指针的类型:类型* const,在成员函数中不能给this指针赋值。

const只修饰其后的变量,至于const放在类型前还是类型后并没有区别。

●只能在成员函数的内部使用。
●this指针本质上是成员函数的形参,当对象调用成员函数时,将对象的地址作为实参传递给形参。对象中不存储this指针。
●this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递。

 

this指针两问

●this指针存放在哪里?

答:this指针本质上是成员函数的形参,一般是存在栈帧中(vs下进行了优化,使用ecx寄存器传递)。

●this指针可以为空吗?

答案:可以

例题1.下面程序编译运行结果是?

class A
{
public:
 void Print()
 {
 cout << "Print()" << endl;
 }
private:
 int _a;
};
int main()
{
 A* a1 = nullptr;
 a1->Print();
 return 0;
}
A、编译报错 B、运行崩溃 C、正常运行
答:正常运行
分析:在上面的代码中,虽然通过空指针调用了成员函数,但是成员函数的地址不在对象中。而是在公共代码区域!
例题2. 下面程序编译运行结果是?
class A
{ 
public:
    void PrintA() 
   {
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}
A、编译报错 B、运行崩溃 C、正常运行
答:运行崩溃
分析:看起来和第一题很像,但是在成员函数中访问了p对象的成员变量,空指针解引用,程序会崩溃。

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

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

相关文章

【Javassist】快速入门系列03 使用Javassist实现方法异常处理

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 文章目录系列文章目录前言引入Javassist jar包使用Javassist实现方法异常处理总结说明前言 上一章我们介绍了使用使用Javassist实现了对方法执行时间的…

07_哈希表

哈希表 1.为什么需要构建哈希表 现在有一组数据&#xff0c;我们想查找一个值&#xff08;x&#xff09;是否在这组数据中&#xff0c;通常来说&#xff0c;我们需要把这组数据遍历一遍&#xff0c;来看看有没有x这个值。 这时&#xff0c;我们发现这样查找数据要花费的时间…

C++ 类和对象

C认为万事万物都皆为对象&#xff0c;对象上有其属性和行为&#xff0c;C面向对象的三大特性为&#xff1a;封装、继承、多态。 一. 封装 封装是C面向对象三大特性之一。 封装的意义&#xff1a; 将属性和行为作为一个整体&#xff0c;表现生活中的事物将属性和行为加以权限控…

【数据库】时间戳并发控制

Timestamp ordering(T/O) 根据事务的时间戳来决定顺序。 如果T1 的时间戳小于T2 的时间戳&#xff0c;那么执行的结果要等价于T1 执行早于T2 的执行。 时间戳的实现策略&#xff1a; 系统时钟 逻辑计数 混合方法 Basic Timestamp Ordering&#xff08;T/O&#xff09;Prtot…

【WPF绑定2】 ComboBox MVVM SelectedValue复杂数据类型绑定

前言 这次绑定是一次非常痛苦的经历&#xff0c;因为SelectedValue总是不能生效&#xff01;我一度怀疑是wpf的Bug。其实还是自己没搞清楚。 在之前的一篇文章中&#xff1a; http://t.csdn.cn/A4W6Ahttp://t.csdn.cn/A4W6A我也写个ComboBox的绑定&#xff0c;但是当时没有指…

【实时数仓】DWM层订单宽表之实现基本的维度查询、加入旁路缓存模式

文章目录一 DWM层-订单宽表1 维表关联代码实现&#xff08;1&#xff09;首先实现基本的维度查询功能a 封装Phoenix查询的工具类PhoenixUtilb 封装查询维度的工具类DimUtil&#xff08;2&#xff09; 优化1&#xff1a;加入旁路缓存模式a 缓存策略的几个注意点b 缓存的选型c 在…

AnimateGAN 迁移部署

文章目录1. 模型概述2. 迁移过程2.1 将ckpt的权重文件转换为pb的权重文件。2.2 将pb的权重文件迁移为 BM1684 bmodel模型2.3 迁移后pipeline搭建2.4 使用streamlit部署3. 效果展示AnimateGAN 是一个基于 GAN 的动漫生成模型&#xff0c;可以将真实的场景照片转换成动漫形式。本…

CASA(Carnegie-Ames-Stanford Approach)模型

植被作为陆地生态系统的重要组成部分对于生态环境功能的维持具有关键作用。植被净初级生产力&#xff08;Net Primary Productivity, NPP&#xff09;是指单位面积上绿色植被在单位时间内由光合作用生产的有机质总量扣除自养呼吸的剩余部分。植被NPP是表征陆地生态系统功能及可…

设计模式之美总结(创建型篇)

title: 设计模式之美总结&#xff08;创建型篇&#xff09; date: 2022-11-03 13:58:36 tags: 设计模式 categories:技术书籍及课程 cover: https://cover.png feature: false 文章目录1. 单例模式&#xff08;Singleton Design Pattern&#xff09;1.1 为什么要使用单例&…

如何在高密度的IB学习中杀出重围?

建议选择IB所需具备的能力/特点 ▣ 敢于挑战自我&#xff0c;愿意通过努力换取个人能力的飞跃 ▣ 如果擅长或喜欢写作&#xff08;中英文&#xff09;&#xff0c;IB对于你来说可能不会那么难。 ▣ 有自主学习、自主研究的能力。有些老师可能教的并不太让人满意&#xff0c;因此…

OpenTelemetry系列 (三)| 神秘的采集器 - Opentelemetry Collector

前言 上个篇章中我们主要介绍了OpenTelemetry的客户端的一些数据生成方式&#xff0c;但是客户端的数据最终还是要发送到服务端来进行统一的采集整合&#xff0c;这样才能看到完整的调用链&#xff0c;metrics等信息。因此在这个篇章中会主要介绍服务端的采集能力。 客户端数…

学Python能做哪些副业?我一般不告诉别人

前两天一个朋友找到我吐槽&#xff0c;说工资一发交完房租水电&#xff0c;啥也不剩&#xff0c;搞不懂朋友圈里那些天天吃喝玩乐的同龄人钱都是哪来的&#xff1f; 确实如此&#xff0c;刚毕业的大学生工资起薪都很低&#xff0c;在高消费、高租金的城市&#xff0c;别说存钱…

日志篇- ES+Logstash+Filebeat+Kibana+Kafka+zk 安装配置与使用详解

1- 学习目标 ELK基本概念&#xff0c;特点安装部署 Kibana ES集群 Logstash Filebeat Kafka集群性能瓶颈以及优化QA汇总 2- 介绍 2.1- 基本概念 Elasticsearch 分布式搜索和分析引擎&#xff0c;具有高可伸缩、高可靠和易管理等特点。基于 Apache Lucene 构建&#xff0c…

xv6---Lab4 traps

参考&#xff1a; Lab: Traps 关于寄存器s0和堆栈https://pdos.csail.mit.edu/6.828/2020/lec/l-riscv-slides.pdf RISC-V assembly Q: 哪些寄存器包含函数的参数?例如&#xff0c;哪个寄存器在main对printf的调用中保存了传参13 ? A: a2保存13(通过gdb调试可看出寄存器a2的…

【设备管理系统】如何助力制造企业实现精益生产?

随着企业对于机械设备的依赖性越来越高&#xff0c;生产设备日益大型化、自动化&#xff0c;流程线生产流程问题逐渐浮于表面&#xff0c;现阶段设备管理的各项制度已经不能够满足日常的生产工作。企业逐渐都面临着设备管理的复杂问题&#xff0c;尤其是设备的保养、维修、日常…

JMeter—HTTP压测

目录&#xff1a;导读 一、创建线程组 二、添加HTTP 三、查看结果树 四、响应断言 五、聚合报告 六、自定义变量 七、CSV可变参数压测 结语 一、创建线程组 右击-->添加-->Threads(Users)-->线程组 下面对比较重要的几个参数&#xff0c;讲解下&#xff1a; …

Vue基础7

Vue基础7生命周期引出生命周期用css animation实现用定时器实现错误&#xff1a;用methods实现使用生命周期函数mounted实现生命周期定义分析生命周期挂载流程beforeCreate()created()beforeMount()mounted()template的作用更新流程beforeUpdate()updated()销毁流程beforeDestr…

【数据库】二阶段锁

Two-phase locking (2PL) is a concurrency controlprotocol that determines whether a txn can access an object in the database on the fly. The protocol does not need to know all the queriesthat a txn will execute ahead of time. 分为两个阶段&#xff1a; 一阶…

颅内EEG记录揭示人类DMN网络的电生理基础

使用无创功能磁共振成像&#xff08;fMRI&#xff09;的研究为人类默认模式网络&#xff08;DMN&#xff09;的独特功能组织和深远重要性提供了重要的见解&#xff0c;但这些方法在跨多个时间尺度上解决网络动力学的能力有限。电生理技术对于应对这些挑战至关重要&#xff0c;但…

RAID 0 添加新磁盘

1&#xff1a;查看当前可用挂载磁盘 lsblk 2&#xff1a;可见 sda 与 sdb 已被挂载&#xff0c;需要挂载 sdc 和 sdd 由于硬盘的默认分区格式是MBR&#xff0c;这种格式的硬盘支持的最大挂载容量为2T&#xff0c;为了满足我们的要求&#xff0c;需要将硬盘格式转化为MBR&…