初识C++ · 类和对象(上)

news2024/10/5 21:24:10

目录

1.面向过程和面向对象初步认识

2.类的引入

3.类的定义

4.类的访问限定符及封装

4.1 访问限定符

4.2 封装

5.类的作用域

6.类的实例化

7.类的对象大小的计算

8.类成员函数的this指针


1.面向过程和面向对象初步认识

C语言是一门面向过程的语言,注重的是解决问题的过程,举个例子——洗衣服,洗衣服的过程是:拿衣服——放进洗衣机——放洗衣液——放水——打开洗衣机——开始工作——结束——晾衣服,注重整个过程,C++是一门面向对象的语言,注重的是解决问题时候涉及的对象,比如洗衣服的时候涉及到了衣服,洗衣机,水,洗衣液等对象,注重的是对象交互来解决某个问题。


2.类的引入

在C语言中的结构体里面只能包含变量,但是在C++里面觉得结构体里面只能放变量不太方便,于是规定函数也可以放在里面:

struct St
{
	int a;
	int b;
	int c;
	int Add(int x,int y)
	{
		return x + y;
	}
};
int main()
{
	struct St s1;
	cout << s1.Add(1, 2);
	return 0;
}

而在中C++更喜欢使用class来定义类,那么这样有有什么好处呢?
比如我们写个栈的初始化,C语言的写法是定义好之后创建函数传参传栈的指针进去,再进行初始化,C++不用:

class Stack
{
	void StackInit()
	{
		arr = nullptr;
		size = capacity = 0;
	}

	int* arr;
	int size;
	int capacity;

};

如此,直接进行初始化就可以了。


3.类的定义

类的定义使用的关键字是class,定义的时候和结构体一样,花括号后面都有一个分号,class后面跟的就是类名:

class ClassName
{

	//...
};

其中,类体(花括号)里面的变量叫做类的属性或者是成员变量,类体里面的函数叫做类的方法或者成员函数

类中定义函数的时候有两种方法:
一是定义和声明放在一起,如:

class ClassName
{
	int Sub(int ,int )
	{
		return 1;
	}
	//...
};

二是声明和定义放在对应的文件里面,如:

//Stack.h

class Stack
{
public:
	void Print();

private:
	int* arr;
	int size;
	int capacity;

};
//Stack.cpp

void Stack::Print()
{
	cout << "Print" << endl;
}

int main()
{
	Stack s1;
	s1.Print();
	return 0;
}

这里有两个需要注意的地方,一个是头文件里面的public和private,一个是.cpp里面的函数定义。

当定义和声明分离的时候,定义函数的时候要在函数名前面加上类名和两个冒号,这是代表去这个类域里面访问这个函数。

定义变量的时候:class Stack s1 和 Stack s1都是可以的,那么方便起见我们就不写class,反正有没有都是可以的。

一般建议的还是第二种,声明和定义分开的形式,这样代码可读性更大,简洁度更高,其次就是函数命名的一些建议

class Date
{
public: 
	void Init(int year,int month,int minute)
	{
		_year = year;
		_month = month;
		_minute = minute;
	}
private:
	int _year;
	int _month;
	int _minute;
};

命名类里面的变量的时候大多数情况下加一个下划线_,这是因为如上情况的时候避免混淆,初始化的时候,如果变量也是year,那么就是year = year,代码的可读性就非常差,那有人说把参数的year换成y不就行了?

实际上参数的命名最好要有比较明显的实际意义,取名为y这谁知道参数对应的含义是什么呢?


4.类的访问限定符及封装

4.1 访问限定符

上文还有一个注意事项没有介绍,就是public和private的介绍,这是C++中定义类的访问限定符,顾名思义,就是访问变量的时候访问是否会受到限制。

那么这有什么意义呢?

就好比文物展览,如果没有保安,没有文物警戒线等,文物是不大能保存完整的,这时候“访问限定符”就起作用了,可以隔绝一部分没有素质的人。

代码也是如此,C语言中结构体定义好之后就是谁都可以访问它,所以比较考验程序员的素质,C++不一样,C++加了访问限定符,有些变量就不能被随意访问,就起到了一定的保护作用。

默认的是用private修饰,也就是默认是私有的,不让访问。

访问限定符一共有3个,private,public,protected:

关于访问限定符的说明:
1 private和protected在外部不能直接被访问,public可以直接被访问

2 访问限定符的作用域是从该限定符开始一直到下一个限定符出现中的区域

3 class默认限定符为private,struct默认限定符为public(因为要兼容C语言)

4.2 封装

面向对象有三大特性:封装,继承,多态

封装就是利用了访问限定符,数据和使用数据的方法的结合,达到隐藏对象的具体细节和属性,但是可以通过接口的实现和外界进行交互。

封装其实就是管理,让使用者使用该使用的,不该使用的就是不使用,比如电脑,电脑的内部CPU,GPU怎么工作的我们是不用关心,我们关心的是我们使用电脑可以完成什么样的工作,这就是一种封装。


5.类的作用域

类定义好了之后就是单独创建了一个域,这个域叫做类域,和命名空间一样,我们使用里面的成员的时候我们需要指定一下是哪个地方的,比如:

class calculator
{
public :
	int Add(int ,int);
};

int calculator::Add(int x, int y)
{
	return x + y;
}
int main()
{
	calculator c1;
	cout << c1.Add(2,4) << endl;
	return 0;
}

目前我们学了 局部域全局域命名空间域类域,要注意区分。


6.类的实例化

类就是一个模型,对象就是根据这个模型创建出来的,所以类和对象常常放在一起,类的实例化我们就可以理解为:我们有一张设计图纸,我们根据设计图纸创建了一些东西,这个过程就叫做实例化

只有实例化出来的对象才可以"存放东西",因为类是不能存放数据的,图纸怎么存放数据?是吧。

class calculator
{
public :
	int Add(int ,int);
private:
	int _a;
};

int main()
{
	calculator _a = 1;
	return 0;
}

如果直接给数据就会报错了。

那么提个问题:

类里面的成员变量是声明还是定义?

答案是声明,也就是说在类里面告诉了你有这个东西,但是我还没有给它开辟空间,这也是实例化之前不能放数据的一个理由。

那么什么时候是定义呢?

实例化的时候我们开辟了空间,这个时候就是定义,因为我们开辟了空间,可以用来存放数据了。


7.类的对象大小的计算

我们来看下面五个类:

class C1
{
public:
	int Add();
};
class C2
{
private:
	int _a;
};
class C3
{

};
class C4
{
	char c;
};
class C5
{
public:
	int Add();
	char c;
};

试问三个类的大小是多大?

第一个类有函数,第二个类有一个int,第三个类是空类。

问题在于:函数算不算进去?

因为类是结构体演化而来的,所以计算的是套用结构体的内存对齐。

空类的内存是1,这是因为空类里面没有东西,为了存这个类的地址,所以必须开辟一个字节用来保存它的地址,但是不存储数据。

C2 和 C3对比就知道,一个是4字节,一个是1字节,因为一个是int,一个是char。

但是C1和C3对比,都是一个字节,但是函数的内存不可能是一字节,所以C1实际上也是空类,这里结合C5和C1也知道函数是不占内存空间的。

所以计算类的大小的时候函数不算进大小,其次就是空类的大小是1,这个1是用来存储类的地址的,其余就是结构体的内存对齐了。


8.类成员函数的this指针

class Cl
{
public:
	int Add(int ,int);
private:
	int _a;
	char _c;
};
int Cl::Add(int x,int y)
{
	return x + y;
}
int main()
{
	Cl c1;
	int ret1 = c1.Add(1, 3);
	Cl c2;
	int ret2 = c2.Add(2, 5);
	return 0;
}

先来看这样一段代码,提问:调用的函数是一个函数吗?还是说调用的是不同对象中的函数?

如果调用的是不同的函数,也就是内存为函数开辟了两个函数栈帧,那么汇编代码中的反汇编call对应的函数的地址应该是不同的,那么:

在汇编代码中,对应的地址是一样的,说明调用的函数是一个,这也可以解释前面的为什么空类里面有一个函数的时候对应的大小却是1,因为函数放在的区域是公共代码段,所以计算大小的时候函数的大小不算在里面。

那么问题来了,函数只有一个,系统是怎么使用函数的,一个函数怎么应用不同的参数呢?

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

void Cl::Print(int x)
{
	cout << x << endl;
}
void Cl::Print(Cl* this,int x)
{
	cout << this->x << endl;
}

就像如此,通过指针来实现各种操作,也就达到了一个函数多个对象使用的目的。

但是this指针实现的时候都是透明的,我们不需要手动参与,编译器会自己实现。

下面来看两个关于this指针的题目:

class A
{
public :
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}
class A
{
public :
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

问:编译结果是否正常?

第一个代码的编译结果是正常的,虽然类指针是空指针,但是访问的不是成员变量,那么实际上的空指针就不会用上,那么this指针是空指针是没问题的,没用到空指针程序就不会崩溃。

第二个代码就会崩溃,因为this指针接收了传过来的空指针,我们又用到了这个空指针访问成员变量,程序就会崩溃了。

	void Print()
	{
		cout << this << endl;
		//cout << _a << endl;
	}

this指针打印出来就是空的。


感谢阅读!

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

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

相关文章

ASP.Net添加Swagger注释

文章目录 Swagger添加Swagger注释 Swagger 添加Swagger注释 1、右击项目->选择属性->点击生成->输出&#xff0c;选中文档文件 2、配置服务 在program.cs 文件里配置SwaggerUI //增加项一 builder.Services.AddSwaggerGen(c> {c.SwaggerDoc("v1", ne…

pytorch 演示 tensor并行

pytorch 演示 tensor并行 一.原理二.实现代码 本文演示了tensor并行的原理。如何将二个mlp切分到多张GPU上分别计算自己的分块,最后做一次reduce。 1.为了避免中间数据产生集合通信,A矩阵只能列切分,只计算全部batch*seqlen的部分feature 2.因为上面的步骤每张GPU只有部分featu…

Leetcode刷题-哈希表详细总结(Java)

哈希表 当我们想使⽤哈希法来解决问题的时候&#xff0c;我们⼀般会选择如下三种数据结构。 数组set &#xff08;集合&#xff09;map&#xff08;映射&#xff09; 当我们遇到了要快速判断⼀个元素是否出现集合⾥的时候&#xff0c;就要考虑哈希法。如果在做⾯试题⽬的时候…

搭建好WordPress网站后的基本操作流程

考虑到很多朋友是第一次使用WordPress&#xff0c;这里给大家分享一下基本的WordPress操作流程&#xff0c;你可以跟着实际情况决定操作步骤。 1.设置网站SSL安全证书。 我采用的是Hostease家的Linux主机产品&#xff0c;自带免费SSL证书 。支持一键安装wordpress程序。 2.进…

智慧驿站式的“智慧公厕”,给城市新基建带来新变化

随着智慧城市建设的推进&#xff0c;智慧驿站作为一种多功能城市部件&#xff0c;正逐渐在城市中崭露头角。这些智慧驿站集合了智慧公厕的管理功能&#xff0c;为城市的新基建带来了全新的变革。本文以智慧驿站智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;大量精品案…

MyBatis操作数据库(1)

前言 在应用分层的学习时, 我们了解到web应用程序一般分为三层,即Controller, Service, Dao. 之前的案例中, 请求流程如下: 浏览器发起请求, 先请求Controller, Controller接受到请求后,调用Service进行业务逻辑处理, Service再调用Dao, 但是Dao层的数据是Mock的, 真实的数据…

基于 Vue3 + Webpack5 + Element Plus Table 二次构建表格组件

基于 Vue3 Webpack5 Element Plus Table 二次构建表格组件 文章目录 基于 Vue3 Webpack5 Element Plus Table 二次构建表格组件一、组件特点二、安装三、快速启动四、单元格渲染配置说明五、源码下载地址 基于 Vue3 Webpack5 Element Plus Table 二次构建表格组件&#x…

【白菜基础】蛋白组学之生信分析(1)

刚换了一个新课题组&#xff0c;新老板的研究方向为蛋白组学&#xff0c;从未接触过蛋白组学的我准备找一组模拟数据进行生信分析的入门学习。 蛋白组学数据挖掘流程图&#xff0c;参考公众号&#xff1a;蛋白质组学数据挖掘思路解析 (qq.com) 一、认识数据 我们组的数据主要…

【高校科研前沿】中国科学院南京地理与湖泊研究所肖启涛博士为一作在Sci. Bull发文:我国湖泊二氧化碳从大气的源向汇转变

目录 1.文章简介 2.研究内容 3.文章引用 1.文章简介 论文名称&#xff1a;Lakes shifted from a carbon dioxide source to a sink over past two decades in China 第一作者及通讯作者&#xff1a;肖启涛&#xff08;博士生&#xff09;&#xff0c;段洪涛&#xff08;研究…

【已解决】HalconDotNet.HOperatorException:“HALCON error #1201: Wrong type of control

前言 最近在学习Halcon视觉和C#的联合开发&#xff0c;碰到一个比较有意思的问题记录一下&#xff0c;大致的报错信息是说我用的halcondotnet版本和我在halcon导出的使用的halcondotnet.dll版本不一致&#xff0c;所以才报错的&#xff01; 解决 首先你得找到你安装halcon的…

接口自动化入门:Jmeter的多组数据测试、JDBC驱动及数据断言!

在进行接口测试时&#xff0c;我们经常需要对接口进行多组数据测试&#xff0c;以验证接口在不同输入条件下的表现。同时&#xff0c;我们也需要对接口返回的数据进行断言&#xff0c;以确保接口返回的数据符合预期结果。JMeter正是一个强大的工具&#xff0c;可以帮助我们实现…

【Linux】正则表达式实验操作实例

正则表达式是一种强大的工具&#xff0c;用于在文本中查找、匹配和替换特定的字符串模式。 实验目的 掌握正则表达式的表达方式掌握grep/egrep命令的用法掌握sed 命令的用法掌握awk命令的用法 正则表达式 实验目的实验内容实验过程创建grep文件来进行如下操作用sed命令完成下列…

寻找排序数组中的最小值

题目描述 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后&#xff0c;得到输入数组。例如&#xff0c;原数组 nums [0,1,2,4,5,6,7] 在变化后可能得到&#xff1a; 若旋转 4 次&#xff0c;则可以得到 [4,5,6,7,0,1,2]若旋转 7 次…

如何水出第一篇SCI:SCI发刊历程,从0到1全过程经验分享!!!

如何水出第一篇SCI&#xff1a;SCI发刊历程&#xff0c;从0到1全路程经验分享&#xff01;&#xff01;&#xff01; 详细的改进教程以及源码&#xff0c;戳这&#xff01;戳这&#xff01;&#xff01;戳这&#xff01;&#xff01;&#xff01;B站&#xff1a;Ai学术叫叫兽e…

机器学习(30)

文章目录 摘要一、文献阅读1. 题目2. abstract3. 网络架构3.1 Sequence Generative Adversarial Nets3.2 SeqGAN via Policy Gradient3.3 The Generative Model for Sequences3.4 The Discriminative Model for Sequences(CNN) 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过…

UWB 雷达动目标检测

1. 静态载波滤除 1. 首先对所有接收脉冲求平均得出参考接收脉冲 [Cir数据为二维数组64*n&#xff0c; 其中n为慢时间域采样的数据帧数] 2. 接着利用每一束接收脉冲减去参考接收脉冲就可以得到目标回波信号&#xff0c;参考接收脉冲的表达式为 2. RD 谱 对雷达回波做静态载波滤…

Linux:IO多路转接之epoll

文章目录 epoll历史epoll的接口epoll_createepoll_waitepoll_ctl epoll原理代码实验 前面的内容介绍了select多路转接&#xff0c;也分析了其利弊&#xff0c;后面用poll改良了select&#xff0c;解决了部分的缺点&#xff0c;但是对于一些核心的缺点还是不能保证&#xff0c;比…

Langchain教程 | langchain+OpenAI+PostgreSQL(PGVector) 实现全链路教程,简单易懂入门

前提&#xff1a; 在阅读本文前&#xff0c;建议要有一定的langchain基础&#xff0c;以及langchain中document loader和text spliter有相关的认知&#xff0c;不然会比较难理解文本内容。 如果是没有任何基础的同学建议看下这个专栏&#xff1a;人工智能 | 大模型 | 实战与教程…

品牌定位升级|飞雕开关如何从家庭作坊走上国际之路?

飞雕电器,这个名字在中国开关插座行业中如同一面旗帜,自1987年起就扬帆在电工领域的大海中。它不仅见证了这个行业的起起伏伏,还始终以其创新的姿态站在浪尖之上。 飞雕的产品线丰富多彩,除主营的墙壁开关插座领域外,飞雕电器还涉足了与墙壁开关紧密相关的其它领域,现已推出移…

vmware 中的Ubuntu系统虚拟机忘记root密码强制重置操作

忘记密码情况下&#xff0c;vmware虚拟机重置Ubuntu的root密码 在企业使用的vmware ESXI中重置Ubuntu系统root密码 1-本地电脑安装个人版的vmware workstation&#xff0c;目的&#xff1a;vmware ESXI自带的远程控制台无法输入指定的键盘按键&#xff0c;需要借助外部的远程辅…