C++---类与对象一

news2024/9/20 15:29:54

类的定义

class className{
	//成员字段
	//成员函数
};

  • class定义类的关键字,className是自己决定的类名,{ } 为类的主体,花括号里是类的内容。类的内容大致分为类的成员属性(变量)和类的成员函数
  • 注意定义类后面需要跟;,与C语言中定义结构体一样。Java中类就不用;

下面以一个栈类举例:

#include<cstdlib>
class Stack {
#define DEFAULT_CAPACITY 4
private:
	int* _arr;
	int _top;
	int _capacity;
public:
	Stack(int capacity = DEFAULT_CAPACITY) {
		_arr = (int*)malloc(DEFAULT_CAPACITY * sizeof(int));
		_top = 0;
		_capacity = capacity;
	}
	void push(int val) {
		if (_top == _capacity) {
			int* tmp = (int*)realloc(_arr,2 * _capacity * sizeof(int));
			if (tmp == nullptr) {
				exit(-1);
			}
			_arr = tmp;
			_capacity = 2 * _capacity;
		}
		_arr[_top++] = val;
	}
	bool isEmpty() {
		return _top == 0;
	}
	int pop() {
		if (isEmpty()) {
			return 0xFFFF;
		}
		return _arr[--_top];
	}
	int peek() {
		if (isEmpty()) {
			return 0xFFFF;
		}
		return _arr[_top-1];
	}
	~Stack() {
		free(_arr);
	}
};
//#include<iostream>
int main(void) {
	Stack stack;//创建一个类变量(对象)
	stack.push(1);//类似中的结构体引用字段,这里同样的方式调用类中函数。
	stack.push(2);
	int ret = stack.pop();
	std::cout<< ret << std::endl;//输出: 2
	return 0;
}

C++中,并没有规定成员函数和成员变量的位置。推荐函数和字段分开写。

//这是三个成员字段。
private:
	int* _arr;
	int _top;
	int _capacity;

为什么变量命名前面加_?
考虑若是我们取消了下划线,采用如下命名:

Stack(int capacity = DEFAULT_CAPACITY) {
	arr = (int*)malloc(DEFAULT_CAPACITY * sizeof(int));
	top = 0;
	capacity = capacity;
}

局部作用域中的capacity覆盖了类里面的capacity,那么capacity = capacity等价于局部变量自己和自己赋值,对类中的capacity字段没有任何影响,故命名风格改为下划线打头(一种风格)。

C++中的struct

C++是C语言的超集, C++将C中的struct升级成了类,那么现在可以粗略认为class等价于struct
主要区别在于默认的访问权限和继承权限:访问权限下文叙述。
struct 升级成了类,那么意味着:

  1. 类可以定义函数
  2. 类表示类型。
//定义一个链表类---普通的单链表
struct list {
	//定义链表节点
	struct Node {
		int val;
		Node* next;
	};
	Node *head;//头指针
	//......
};

int main() {
	// 不需要sizeof(struct list),可以省略struct,类名就是类型 
	std::cout<<"sizeof(list):"<<sizeof(list) << std::endl;
	return 0;
}

类成员函数的内联性

类内定义的成员函数

  • 当成员函数在类内部定义时,编译器默认将其视为 inline。这种定义方式允许编译器在每个调用点直接插入函数代码,从而可能提高性能。
class MyClass {
public:
    void inlineFunction() { // 默认 inline
        std::cout << "Inline function" << std::endl;
    }
};

C++通常将类的声明和定义分开放置,有助于实现接口和实现的分离。
类中放函数定义(提供接口),成员函数在外部实现。

访问修饰符

在这里插入图片描述
C++实现封装的方式:类将对象的属性与方法结合在,让对象更加完善,通过访问权限允许用户使用。

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员类外不能直接被访问,类中可以使用(protected具体在继承中说明)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到} 即类结束。
  5. class的默认访问权限为private,struct为public(struct要兼容C)

类域

C++中,用{ }括起来的都会形成一个域。类定义了一个新的作用域: 类域,类的所有成员都在类的作用域中。
命名冲突在C语言中很明显, C++中类域有效解决了问题。比如C中自定义的Stack和Queue若都定义了size函数,那么如果你想同时引用两个头文件,不可豁免的出现了命名冲突。这时候,就不得不做出折中,选择size方法加上标识以区分。

任何一个变量,编译器都会去找他的出处。编译器默认优先在局部域寻找,然后是全局域,不会去类域中找。类外定义成员时,要使用 : : 作用域操作符指明成员属于哪个类域.


class Date
{
public:
	void Init(int year, int month, int day);


private:
	int _year;
	int _month;
	int _day;
};


void Date::Init(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

类域影响的编译器查找规则,不指定类域,Init函数会被全局函数。
域名限定修饰符中的Init是Date类中的成员函数, 那么既然是类中的函数,那么可以引用类的成员字段。

实例化对象

用类类型创建对象的过程,称为类的实例化

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没
    有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个
    类,来描述具体学生信息。
    类就像谜语一样,对谜底来进行描述,谜底就是谜语的一个实例。
    谜语:“年纪不大,胡子一把,主人来了,就喊妈妈” 谜底:山羊
  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
    Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄。
  3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设
    计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象
    才能实际存储数据,占用物理空间。
class Date
{
public:
	void Init(int year, int month, int day);


private:
//只是声明类中有这个字段,并没有申请空间。
	int _year;
	int _month;
	int _day;
};
int main(void) {
	//用类这个类型创建一个变量的过程叫做实例化对象。
	Date date;//只要实例化对象,才会分配空间
	date.Init(2024, 9, 18);
	return 0;
}

计算类对象的大小

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

问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算
一个类的大小?
类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。再分析⼀下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量 _year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。

其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指 令[call 地址],其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找.
只有动态多态是在运行时找,就需要存储函数地址。

// 类中既有成员变量,又有成员函数
class A1 {
public:
	void f1() {}
private:
	int _a;
};

class A2 {
public:
	void f2() {}
};
// 类中什么都没有---空类
class A3
{};
int main(void) {
	std::cout << "A1:" << sizeof(A1) << " A2:" << sizeof(A2) << " A3:" << sizeof(A3) << std::endl;
	return 0;
}

输出结果:A1:4 A2:1 A3:1
结论:一个类的大小,实际就是该类中”成员变量”之和,但需注意内存对齐 空类比较特殊,编译器给了空类“一个字节”来唯一标识这个类的对象。

结构体内存对齐

这里只是列出C语言处学的结构体内层对齐规则。

  1. 第一个成员在与结构体偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8
  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

this指针

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

对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函
数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
在 C++ 中,调用一个对象的成员函数时,编译器自动将调用该函数的对象地址通过 this 指针传递给该函数。这个 this 指针指向当前调用函数的对象实例,从而让成员函数知道它应该操作哪个对象的成员变量。
具体来说, Date 类中,Init 和 Print 函数其实都隐含了对 this 指针的使用。编译器在编译时,会自动将 this 指针传递给这两个成员函数,使得它们能够操作调用它们的对象的成员变量。下面通过对代码的解释来说明:

class Date
{
public:
    void Init(int year, int month, int day)
    {
        // 隐含 this 指针:this->_year, this->_month, this->_day
        _year = year;
        _month = month;
        _day = day;
    }
    
    void Print() //隐含于void Print(Date *this)
    {
        // 隐含 this 指针:this->_year, this->_month, this->_day
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

this指针的特性

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
    this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
    递,不需要用户传递.
  1. this指针存在哪里?
    函数栈帧的局部变量中,或者在函数内。
  2. this可以为nullptr?
    在C++中this指针不能为nullptr,它始终指向当前对象(而当前对象是已分配有效地址的变量)。调用非静态成员函数时,this指针必须指向有效的对象实例。
    如果内部没有出现类似this->?操作,那么程序不会崩溃,但这种本身就是未定义行为。

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

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

相关文章

SpringBoot - 基于 Java的超市进销存系统

专业团队&#xff0c;咨询就送开题报告&#xff0c;欢迎大家私信&#xff0c;留言&#xff0c;联系方式在文章底部 摘 要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;超市进销存系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xf…

【JUC】17-Synchronized锁升级

1. 锁分类 无锁->偏向锁->轻量级锁->重量级锁 synchronized属于重量级锁&#xff0c;monitor是基于底层os的mutex Lock实现了&#xff0c;挂起线程和恢复线程都需要内核态完成&#xff0c;都需要切换CPU状态来完成。 Monitor与对象以及线程如何关联&#xff1f;  1…

OV-DINO:统一开放词汇检测与语言感知选择性融合

文章目录 摘要1、引言2、相关工作3、方法3.1、概述3.2、统一数据集成3.3、语言感知选择性融合3.4、以检测为中心的预训练 4、实验4.1、预训练数据和评估指标4.2、实施细节4.3、主要结果4.4、消融研究4.5、定性结果 5 、讨论 摘要 开放词汇检测&#xff08;Open-vocabulary Det…

滑动窗口(6)_找到字符串中所有字母异位词

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 滑动窗口(6)_找到字符串中所有字母异位词 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f4…

《SmartX ELF 虚拟化核心功能集》发布,详解 80+ 功能特性和 6 例金融实践

《SmartX ELF 虚拟化核心功能集》电子书现已发布&#xff01;本书详细介绍了 SmartX ELF 虚拟化及云平台核心功能&#xff0c;包含虚机服务、容器服务、网络服务、存储服务、运维管理、工具服务、数据保护等各个方面。 即刻下载电子书&#xff0c;了解如何利用基于 SmartX ELF …

助力电商升级,智象未来(HiDream.ai)开启未来商业新篇章

近日&#xff0c;智象未来&#xff08;HiDream.ai&#xff09;凭借其创新性的“秩象™大模型”&#xff0c;在业界掀起了一场跨行业的创意革命&#xff0c;对视觉设计、运营商服务、品牌营销以及文旅传媒等领域的创新发展产生了深远影响。致力于全球领先的多模态生成式人工智能…

neo4j节点关联路径的表示、节点的增删改查

目录 核心概念节点的增删改查&#xff08;1&#xff09;增&#xff08;2&#xff09;查&#xff08;3&#xff09;删&#xff08;4&#xff09;改 neo4j文档&#xff1a;https://neo4j.com/docs/ https://neo4j.com/docs/cypher-manual/current/introduction/ 核心概念 节点 ne…

【从计算机的发展角度理解编程语言】C、CPP、Java、Python,是偶然还是应时代的产物?

参考目录 前言什么是"computer"?计算机的大致发展历程计算机系统结构阶段(1946~1981)计算机网络和视窗阶段(1982~2007)复杂信息系统阶段(2008~today)人工智能阶段 越新的语言是越好的吗、越值得学习吗&#xff1f; 前言 最近读了 《Python语言程序设计基础》 这本书…

Linux运维篇-服务器简介

目录 前言服务器分类&#xff08;按服务器的机箱结构来划分&#xff09;台式服务器机架式服务器刀片式服务器 外观部件内部结构前面板前面板组件前面板接口说明前面板指示灯和按钮前面板指示灯/按钮说明 后面板后面板组件后面板接口说明后面板指示灯后面板指示灯说明 主板和 iB…

C#|.net core 基础 - 值传递 vs 引用传递

不知道你在开发过程中有没有遇到过这样的困惑&#xff1a;这个变量怎么值被改&#xff1f;这个值怎么没变&#xff1f; 今天就来和大家分享可能导致这个问题的根本原因值传递 vs 引用传递。 在此之前我们先回顾两组基本概念&#xff1a; 值类型** vs 引用类型** **值类型&a…

适合金融行业的银行级别FTP替代升级方案

在数字化办公日益普及的今天&#xff0c;金融领域对数据传输的需求日益增长&#xff0c;场景也变得更加多样化和复杂。这不仅包括内部协作&#xff0c;还涉及金融服务、外部合作以及跨境数据流动等方面。因此&#xff0c;金融行业对数据传输系统的要求越来越高&#xff0c;传统…

LeetCode 算法笔记-第 04 章 基础算法篇

1.枚举 采用枚举算法解题的一般思路如下&#xff1a; 确定枚举对象、枚举范围和判断条件&#xff0c;并判断条件设立的正确性。一一枚举可能的情况&#xff0c;并验证是否是问题的解。考虑提高枚举算法的效率。 我们可以从下面几个方面考虑提高算法的效率&#xff1a; 抓住…

孙怡带你深度学习(3)--损失函数

文章目录 损失函数一、L1Loss损失函数1. 定义2. 优缺点3. 应用 二、NLLLoss损失函数1. 定义与原理2. 优点与注意3. 应用 三、MSELoss损失函数1. 定义与原理2. 优点与注意3. 应用 四、BCELoss损失函数1. 定义与原理2. 优点与注意3. 应用 五、CrossEntropyLoss损失函数1. 定义与原…

『 Linux 』HTTP(一)

文章目录 域名URLURLEncode和URLDecodeHTTP的请求HTTP的响应请求与响应的获取简单的Web服务器 域名 任何客户端在需要访问一个服务端时都需要一个IP和端口号,而当一个浏览器去访问一个网页时通常更多使用的是域名而不是IP:port的方式, www.baidu.com这是百度的域名; 实际上当浏…

数据结构和算法|排序算法系列(五)|排序总结(时间复杂度和是否稳定)

文章目录 选择排序冒泡排序插入排序快排归并排序堆排序 选择排序 一句话总结&#xff0c;开启一个循环&#xff0c;每轮从未排序区间选择****最小的元素&#xff0c;将其放到已排序区间的末尾。「未排序区间一般也放在后面&#xff0c;已排序区间放在前面」 选择排序 时间复…

2024蓝桥杯省B好题分析

题解来自洛谷&#xff0c;作为学习 目录 宝石组合 数字接龙 爬山 拔河 宝石组合 # [蓝桥杯 2024 省 B] 宝石组合## 题目描述在一个神秘的森林里&#xff0c;住着一个小精灵名叫小蓝。有一天&#xff0c;他偶然发现了一个隐藏在树洞里的宝藏&#xff0c;里面装满了闪烁着美…

Flutter Android Package调用python

操作步骤 一、创建一个Flutter Package 使用以下指令创建一个Flutter Package flutter create --templateplugin --platformsandroid,ios -a java flutter_package_python 二、修改android/build.gradle文件 在buildscript——>dependencies中添加以下内容 //导入Chaqu…

接口幂等性和并发安全的区别?

目录标题 幂等性并发安全总结 接口幂等性和并发安全是两个不同的概念&#xff0c;虽然它们在设计API时都很重要&#xff0c;但侧重点不同。 幂等性 定义&#xff1a;幂等性指的是无论对接口进行多少次相同的操作&#xff0c;结果都是一致的。例如&#xff0c;HTTP的PUT和DELE…

TON基金会与Curve Finance合作:推出基于TON的新型稳定币互换项目

2024年&#xff0c;TON基金会宣布与去中心化金融&#xff08;DeFi&#xff09;领域的知名协议Curve Finance建立战略合作&#xff0c;携手推出一个全新的基于TON区块链的稳定币交换项目。这一合作标志着TON生态系统在DeFi领域的进一步扩展&#xff0c;并将通过Curve Finance的核…

玖逸云黑系统源码 v1.3.0全解无后门 +搭建教程

功能带有卡密生成和添加黑名单等&#xff0c;反正功能也不是很多具体的自己看程序截图即可。 搭建教程 完成 1.我们先添加一个站点 2.PHP选择7.3 3.上传源码解压 4.导入数据库 5.配置数据库信息config.php 源码下载&#xff1a;https://download.csdn.net/download/m0_6…