首页 > 其他分享 >类与对象(一)

类与对象(一)

时间:2024-07-21 23:54:49浏览次数:6  
标签:p2 对象 void int Init 对齐 成员

目录

一. 类

1. 类的定义

 2. 访问限定符

 3. 类域

二. 实例化

1. 实例化的概念

2. 对象的大小

三. this指针

使用

注意


本篇文章主要讲述

实例化

this指针

等问题

一. 类

1. 类的定义

如以下代码

class为定义类的关键字Stack是类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数

#include<iostream>
using namespace std;
class Stack
{
public:
	//成员函数
	void StackInit(int n=4)
	{
		arr = (int*)malloc(sizeof(int) * n);
		capacity = 0;
		Top = 0;
	}

	void StackPush(int data)
	{
		if (Top == capacity)
		{
		int newcapacity = capacity == 0 ? 4 : capacity * 2;
		int* tmp = (int*)realloc(arr, sizeof(int) * newcapacity);
		if (tmp == NULL)
		{
			perror("relloc fail");
			return;
		}
		arr = tmp;
		capacity = newcapacity;
		}
		arr[Top] = data;
		Top++;
	}
	int StackTop()
	{
		return arr[Top - 1];
	}

	void StackDestroy()
	{
		arr = NULL;
		capacity = 0;
		Top = 0;
	}
private:
	int* arr;
	int Top;
	int capacity;
};

int main()
{
	Stack st;
	st.StackInit();
	st.StackPush(6);
	st.StackPush(9);
	cout << st.StackTop() << endl;
	st.StackDestroy();
	return 0;
}

为了区分成员变量,一般习惯上 成员变量会加一个特殊的标识,并不是C++强制的,具体看企业的要求

 如下定义

#include<iostream>
using namespace std;
class Stack
{
public:
	int* _arr;
	int _Top;
	int _capacity;
	//int* m_arr;
	//int m_Top;
	//int m_capacity;
};

 C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的区别为struct中可以定义函数了,并且在定义中不必须加struct

如下代码所示,既使用了函数又直接用Pc定义了p1

struct Pc
{
public:
	void Init()
	{
		a = 10;
		b = 11;
		c = 13;
	}
	int a;
	int b;
	int c;
};

int main()
{
	Pc p1;//struct可以省略
	struct Pc p2;
	p1.Init();
	p2.b = 33;
	p2.c = 44;
	p2.a = 22;
	cout << p1.a << " " << p1.b << " " << p1.c << endl;
	cout << p2.a << " " << p2.b << " " << p2.c << endl;
	return 0;
}

定义在类里面的成员函数默认为inline

 2. 访问限定符

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

public(公共权限)可以在类外直接被访问
private(私有权限)类外不可以直接访问(继承中子不可以访问父亲私有内容)
protected(保护权限)类外不可以直接访问(继承中子可以访问父亲的安全内容)

访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止,如果后面没有访问限定符,作用域就到 } 即类结束的时候

class Pc
{
public:
	void Init()
	{
		a = 10;
		b = 11;
		c = 13;
	}
private:
	int a;
	int b;
	int c;

};

int main()
{

	Pc p2;
	p2.Init();
	p2.b = 33;
	p2.c = 44;
	p2.a = 22;
	cout << p2.a << " " << p2.b << " " << p2.c << endl;
	return 0;
}

我们发现上述代码编译器会报错,因为调用了私有的成员变量,但是调用Init函数是可以的,因为函数处于public作用域下

class定义成员没有被访问限定符修饰时默认为private,struct定义成员没有被访问限定符修饰时默认为public

 如以下代码所示

#include<iostream>
using namespace std;
class Pc1
{
	int a;
	int b;
	int c;
};
struct Pc2
{
	int a;
	int b;
	int c;
};
int main()
{
	Pc1 p1;
	Pc2 p2;
	p1.b = 33;
	p1.c = 44;
	p1.a = 22;
	p2.b = 33;
	p2.c = 44;
	p2.a = 22;
	cout << p1.a << " " << p1.b << " " << p1.c << endl;
	cout << p2.a << " " << p2.b << " " << p2.c << endl;
	return 0;
}

我们发现再给p1中的成员赋值时会报错

一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会变为public

 3. 类域

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

 如下代码所示

#include<iostream>
using namespace std;

class Pc1
{
	int a;
	int b;
	int c;
public:
	void Init();
	void Printf();
};
void Pc1::Init()
{
	a = 11;
	b = 12;
	c = 13;
}
void Pc1::Printf()
{
	cout << a << " " << b << " " << c << endl;
}
int main()
{
	Pc1 p1;
	p1.Init();
	p1.Printf();
	return 0;
}

以上代码两个函数都是声明在类里,定义在类外用::域作用限定符指明了成员属于哪个类域。

如果上述代码不指名呢?

类域影响的是编译的查找规则,上面程序中如果不指定类域Pc1,那么编译器就会将Init与Printf当成全局函数,那编译时就找不到a,b,c等成员的声明/定义在哪,就会报错。指定类域Pc1,就知道了Init与Printf是成员函数,当前域中找不到的成员就会到类域中去查找。

二. 实例化

1. 实例化的概念

用类 类型在物理内存中创建对象的过程,称为类实例化出对象。

#include<iostream>
using namespace std;

class Pc
{
    //这里只是声明并没有开空间
	int a;
	int b;
	int c;
public:
	void Init()
	{
		a = 11;
		b = 12;
		c = 13;
	}
	void Printf()
	{
		cout << a << " " << b << " " << c << endl;
	}
};

int main()
{
	Pc p;//Pc实例化出对象p
    //没有空间不能直接访问
	//Pc::a;
	//Pc::b;
	//Pc::c;

	return 0;
}

上述代码中 在Pc类实例化出对象p是才为Pc中的成员开辟了空间。类就类似一张图纸,可以根据图纸修房子住人,但是其本身不能住人

2. 对象的大小

类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?函数被编译后是一段指令,对象中没有办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。而函数指针是不需要存储的,函数指针是一个地址,调用函数被编译成汇编指令  call地址,其实编译器在编译链接时,就要找到函数的地址,不是在运行中找,只有动态多态是在内存中找,就需要存储函数地址,这个暂且先不讲

 假设同一个类Pc

int main()
{
	Pc p1;
	Pc p2;

	p1.Init();
	p2.Init();
	p1.Printf();
	p2.Printf();
	return 0;
}

我们通过观察反汇编代码可得

在反汇编代码中,call指令通常用于跳转到其他内存地址执行代码。 

 我们发现两个所跳向函数地址相同

C++规定实例化的对象也要符合内存对齐规则

内存对齐规则

1.第一个成员对齐到结构体变量为偏移量0的地址处

2.其他成员要对齐到某个数字(对齐数)的整数倍地址处

对齐数:编译器默认的一个对齐数与该成员占内存之间的较小值  vs默认对齐数为8Linux中gcc没有默认对齐数,对齐数就是成员自身大小

3.结构体的大小为最大对齐数(占用字节最多的成员变量)的整数倍(每个成员变量都有一个对齐数)

4.嵌套结构体的情况:嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

#include<iostream>
using namespace std;

class S1
{
	char _a;
	int _b;
	char _c;
public:
	void Init()
	{
		_a = 11;
		_b = 12;
	}
	void Printf()
	{
		cout << _a << " " << _b << " " << endl;
	}
};
class S2
{
	char c1;
	char c2;
	int i;
public:
	void Printf()
	{
		cout << "1111" << endl;
	}
};

class S3
{
};
int main()
{
	S1 p1;
	S2 p2;
	S3 p3;
	cout << sizeof(p1) << "  " << sizeof(p2) << "  " << sizeof(p3) << endl;

	return 0;
}

模拟一下S1与S2在内存中分别怎么存储的

而S3没有成员变量但是其对象的大小为1,为什么呢?

因为若一个字节也不给,没法表示对象存在过,这里给1字节为了表示对象存在过

三. this指针

使用
#include<iostream>
using namespace std;

class Pc
{
	char _a;
	int _b;
	char _c;
public:
	void Init(int aa = 11, int  bb = 12, int cc = 13)
	{
		_a = aa;
		_b = bb;
		_c = cc;
	}
	void Printf()
	{
		cout << _a << " " << _b << " " << _c << endl;
	}
};

int main()
{
	Pc p1;
	Pc p2;
	p1.Init();
	p2.Init(1, 2, 3);
	p1.Printf();
	p2.Printf();
	return 0;
}

Pc类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用Init和 Print函数时,该函数是如何知道应该访问的是p1对象还是p2对象呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这里的问题

编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。⽐如Pc类的Init的真实原型为, void Init(Date* const this, int year, int month, int day)

 void Init(Date* const this, int aa = 11, int  bb = 12, int cc = 13);

 类中的成员函数访问成员变量,其本质都是通过this指针进行访问,如上述代码Init赋值实际操作为

	void Init(int aa = 11, int  bb = 12, int cc = 13)
	{
		this->_a = aa;
		this->_b = bb;
		this->_c = cc;
	}
注意

C++规定不能在实参和形参的位置显式的写this指针(编译时编译器会处理,如下图所示),但是可以在函数体内显式使⽤this指针


这篇文章就到这里啦,喜欢可以点赞支持

(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

标签:p2,对象,void,int,Init,对齐,成员
From: https://blog.csdn.net/m0_68142120/article/details/140594121

相关文章

  • java面向对象进阶篇--《继承》(万字总结,建议收藏)
    一、前言java部分连载开始,继续开始我们的java篇,前几天一直在调节web项目,刷了点力扣的题,导致java篇拉下了点。希望大家支持一下作者,制作不易。支持一下吧(#^.^#)---------------------------------------->点我❥(^_-) 二、java继承的概念和特点Java中的继承结构指的是通......
  • 第九章面向对象程序设计
    两大编程思想面向过程功能上的封装,典型代表:C语言面次对象属性和行为上的封装:典型代表Java和Pathon步骤确定:面向过程类和对象类:由N多个对象抽取出‘像’的属性和行为从而归纳总结出来的一种类别在Pathon中一切皆对象点击查看代码示例9-1查看对象的数据类型a=10b......
  • Elastic Search基于Spring Boot实现复杂查询和对复杂查询结果的映射银行账户对象并获
    packagecom.alatus.search;importcom.alatus.search.config.MallElasticSearchConfig;importcom.alibaba.fastjson.JSON;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importlombok.ToString;importorg.elasticsearch.......
  • TypeScript与面向对象编程
    引言TypeScript简介TypeScript是JavaScript的一个超集,由微软开发,它在JavaScript的基础上添加了类型系统和对ES6+的新特性的支持。TypeScript最终会被编译成纯JavaScript代码,以便在任何支持JavaScript的环境中运行。面向对象编程(OOP)概念面向对象编程是一种编程范式,它使用“......
  • 面向对象设计的原则有哪些?
    1、单一责任原则(SingleResponsibilityPrinciple,SRP)一个类应该仅有一个引起它变化的原因。换句话说,一个类应该只有一个职责。这有助于保持类的内聚性,降低耦合度。2、开放-封闭原则(Open-ClosePrinciple,OCP)软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改的。......
  • Python第九章(面向对象基础--属性,继承,dir查看,内存地址,权限等等和银行账户题目,圆的面积
    面向对象创造对象示例代码:类的名字用小驼峰命名法#编写Person类classPerson():passclassCat:#,小括号可以省略pass#对象名=类名per=Person()c=Cat()#小括号不能省略print(type(per))print(type(c))代码结果:<class'__main__.Person'><class'__mai......
  • JS 对象的遍历
    ​定义一个对象,对象的键包含所有数据类型constSymbolKey=Symbol('a')constdict={[66]:66,"string":'string',[true]:true,[undefined]:undefined,[null]:null,[BigInt(123)]:BigInt(123),[function(){console.log("h......
  • vue3 Promise处理异步操作的对象
    Promise是JavaScript中用于处理异步操作的一种对象。它代表了一个异步操作的最终完成(或失败)及其结果值。在处理异步操作时,Promise提供了一种更干净、更可读的方式来管理回调函数。Promise的状态一个Promise对象有三种状态:Pending(进行中):初始状态,操作尚未完成。Fulfille......
  • 04 ES6中对象的简写
    在ES6中,对象字面量的书写方式进行了一些简化,使得对象的创建更加简洁。以下是ES6中对象简写的几种形式:属性值缩写:当对象的属性名和属性值的变量名相同时,可以省略属性值,只写属性名。//ES5constname='Alice';constage=25;constperson={name:name,a......
  • 可包装对象
    std::function模板类是一个通用的可调用对象的包装器,用简单的、统一的方式处理可调用对象。#include<iostream>#include<functional>usingnamespacestd;voidshow(intage,conststring&message){ cout<<"age"<<age<<"message"<<......