首页 > 编程语言 >C++入门——类和对象(上)

C++入门——类和对象(上)

时间:2024-09-22 21:20:07浏览次数:3  
标签:ps capacity 入门 对象 top C++ int Init void

文章目录


一、类的定义

1.1 类的定义格式

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

为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者 m开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求。

class stack
{
	//成员函数
	void Init(int capacity = 4)
	{
		_array = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	void Push(int x)
	{
		// ...扩容
		_array[_top++] = x;
	}
	
	int Top()
	{
		assert(_top > 0);
		return _array[_top - 1];
	}
	void Destroy()
	{
		free(_array);
		_array = nullptr;
		_top = _capacity = 0;
	}
	//成员变量
	int* _array;
	size_t _capacity;
	size_t _top;
};//分号不能省略

例如上面我们定义了栈和栈的一些基本操作,那我们在定义和操作栈时也和C语言类似:

int main()
{
	//类名就是类型,用类型定义对象
	Stack st1;//定义栈
	st1.Init();//初始化栈
	st1.Push(1);//压栈
	st1.Push(2);
	st1.Push(3);

	cout << st1.Top() << endl;//访问栈顶元素

	st1.Destroy();//销毁
	return 0;
}

C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。定义在类⾯的成员函数默认为inline。

1.2 访问限定符

上述对栈的定义中,如果我们仅允许定义的方法可以在类外被访问,不欺往在类外直接访问我们的成员变量,那就需要用到访问限定符。

  • public修饰的成员在类外可以直接被访问;
  • protected和private修饰的成员在类外不能直接被访问
class stack
{
public:
	//成员函数
	void Init(int capacity = 4)
	{
		_array = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	void Push(int x)
	{
		// ...扩容
		_array[_top++] = x;
	}
	
	int Top()
	{
		assert(_top > 0);
		return _array[_top - 1];
	}
	void Destroy()
	{
		free(_array);
		_array = nullptr;
		_top = _capacity = 0;
	}
private:
	//成员变量
	int* _array;
	size_t _capacity;
	size_t _top;
};//分号不能省略

这里如果访问_top,就会报错。

int main()
{
	//类名就是类型,用类型定义对象
	Stack st1;//定义栈
	st1.Init();//初始化栈
	st1.Push(1);//压栈
	st1.Push(2);
	st1.Push(3);
	//st1._top;//报错
	cout << st1.Top() << endl;//访问栈顶元素

	st1.Destroy();//销毁
	return 0;
}
  • 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到 }即类结束。
  • class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
  • ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。

1.3 类域

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

  • 如下面代码,Init函数声明和定义分开
#include<iostream>
using namespace std;

class Stack
{
public:
	//成员
	void Init(int n = 4);
private:
	// 成员变量
	int* _array;
	size_t _capacity;
	size_t _top;
};

void Stack::Init(int n)
{
	_array = (int*)malloc(n * sizeof(int));
	if (_array == nullptr)
	{
		perror("molloc申请失败\n");
		return;
	}
	_capacity = n;
	_top = 0;
}

int main()
{
	Stack st;
	st.Init();

	return 0;
}
  • 类域影响的是编译的查找规则,上面程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。

二、实例化

2.1 实例化概念

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

  • 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
  • 就像给我一张建筑的设计图,它并没有占用土地。类就是我们的设计图,它并没有占用空间。一张设计图可以建造很多房子,同样的,一个类也可以实例化出很多对象。

2.2 对象大小

实例化出的对象,只包含它的成员变量,而成员函数存储在⼀个单独的区域(代码段)。

内存对⻬规则

  • 第⼀个成员在与结构体偏移量为0的地址处。
  • 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
  • 注意:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
  • VS中默认的对⻬数为8
  • 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。
class A
{};

class B
{};

我们看到没有成员变量的A和B类对象的⼤⼩是1,以这⾥给1字节,纯粹是为了占位标识对象存在。

三、this指针

我们来看下面这段代码

#include<iostream>
using namespace std;
class Date
{
public:
	// void Init(Date* const this, int year, int month, int day)
	void Init(int year, int month, int day)
	{
		// 编译报错:error C2106: “=”: 左操作数必须为左值
		// this = nullptr;
		// this->_year = year;
		_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2;

	d1.Init(2024,9,21);
	d1.Print();
	
	d2.Init(2023,9,21);
	d2.Print();
	return 0;
}

在这里插入图片描述

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

  • 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this
    指针。
    ⽐如Date类的Init的真实原型为:
	void Init(Date* const this, int year, int month, int day)
	void Init(int year, int month, int day)
  • 而在调用Init函数时,同样会隐含的传入对象的地址
d1.Init(2024,9,21);
d1.Init(&d1,2024,9,21);//这里调用其实省略了d1的地址,但其实编译器是传入了d1的地址
//但正确的写法是不加&d1的,这里为了看的更明显
  • 同样的,print函数也是如此:
void Print()
	{
		//cout << this->_year << "/" << this->_month << "/" << this->_day << endl
		cout << _year << "/" << _month << "/" << _day << endl;
	}

C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。但一般情况下也不会去加。

思考下面两道题的答案:(答案见评论区)

1.下⾯程序编译运⾏结果是()
A、编译报错 B、运⾏崩溃 C、正常运⾏

#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

2.下⾯程序编译运⾏结果是()
A、编译报错 B、运⾏崩溃 C、正常运⾏

#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

四、C++和C语言实现Stack栈的对比

  • C++实现
#include<iostream>
#include<assert.h>
using namespace std;
//C++实现栈
typedef int SDataType;
class Stack
{
public:
	//成员函数
	void Init(int n = 4)
	{
		_a = (SDataType*)malloc(n * sizeof(SDataType));
		_capacity = n;
		_top = 0;
	}

	void Push(SDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity = 0 ? 4 : 2 * _capacity;
			SDataType* temp = (SDataType*)realloc(_a, newcapacity * sizeof(SDataType));
			if (temp == nullptr)
			{
				perror("realloc fail");
				return;
			}
			_a = temp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;
	}
	void Pop()
	{
		assert(_top > 0);
		--_top;
	}
	bool Empty()
	{
		return _top == 0;
	}
	int Top()
	{
		assert(_top > 0);
		return _a[_top - 1];
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

private:
	//成员变量
	SDataType* _a;
	int _capacity;
	int _top;
};
  • C语言实现
//c语言实现栈
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;
void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}
void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
			sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	ps->top--;
}
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
  • 通过以上两份代码对⽐,我们发现C++实现Stack形态上发⽣了挺多的变化,但是底层和逻辑上没啥变化。
  • C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,这个是最重要的变化。这⾥的封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的问题。当然封装不仅仅是这样的,我们后⾯还需要不断的去学习。
  • C++中有⼀些相对⽅便的语法,⽐如Init给的缺省参数会⽅便很多,成员函数每次不需要传对象地址,因为this指针隐含的传递了,⽅便了很多,使⽤类型不再需要typedef⽤类名就很⽅便

总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了类和对象的一些内容,更多有关类和对象的内容,请关注下一篇文章。

标签:ps,capacity,入门,对象,top,C++,int,Init,void
From: https://blog.csdn.net/m2668465307/article/details/141996798

相关文章

  • C++ 解析 RDP 协议
    远程桌面协议(RemoteDesktopProtocol,RDP)是微软开发的一种网络通信协议,用于提供远程桌面会话服务。它允许用户通过网络连接至远程计算机,并像使用本地计算机一样操作远程系统。本文档将详细探讨在C++环境中如何解析RDP协议,涵盖协议层次解析、连接过程管理、数据加密解密、......
  • C++三大特性——继承性(超万字详解)
    目录前言一、封装1.封装(Encapsulation)二、继承1.构造函数的调用顺序原理:2.析构函数的调用顺序原理:3、派生类的隐藏 1.成员函数隐藏2.成员变量隐藏3.基类函数的重载隐藏三、多重继承问题1.构造函数的调用顺序2.析构函数的调用顺序3.多重继承中的命名......
  • C语言入门到精通(第六版)——第一章
    1.1C语言发展史1.1.1C语言简述    1、机器语言:机器语言是低级语言,也被称为二进制语言,通过01表示的二进制语言编写的代码。机器语言的特点是可以通过计算机直接识别,不需要编译。    2、汇编语言:汇编语言是面向机器的程序设计语言,通过英文或字符串替代二进......
  • 优秀的拆分(csp2020入门级1)
    一般来说,一个正整数可以拆分成若干个正整数的和。例如,1=1,10=1+2+3+4等。 对于正整数n的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下,n被分解为了若干个不同的2的正整数次幂。注意,一个数x能被表示成2的正整数次幂,当且仅当x能通过正整数个2相乘在一起得到。 例如,10......
  • [Mysql]基础入门篇_关于数据库的简介_初识DQL语句
    @[TOC]文章目录何为数据库数据库类型数据库管理系统(本博主使用的是Mysql数据库)何为SQL准备工作及select查询操作的简单使用何为数据库数据库是一个有组织的数据集合,允许高效存储、管理和检索信息。它通过结构化的方式存储数据,通常使用数据库管理系统(DBMS)来执行操作,如......
  • C++中模板的初级使用函数模板(刚刚接触模板概念的小白也能明白)
    文章目录模板分类函数模板函数模板的原理函数模板基本语法——typename以及class简单的函数模板多类型模板参数class和typename的选择类模板模板分类模板的核心思想是让编译器在编译时生成适用于具体类型的代码,这个过程称为模板实例化。C++中的模板分为两种:......
  • C/C++中内存管理
    这里写目录标题C语言中动态内存管理方式:malloc/calloc/realloc/freeC++内存管理方式operatornew与operatordelete函数operatornewoperatordeletenew和delete的实现原理malloc/free和new/delete的区别C语言中动态内存管理方式:malloc/calloc/realloc/free在C语言......
  • C++从入门到起飞之——多态 全方位剖析!
    ......
  • 30分钟从零开始入门 Redis (包含 Redis 安装教程 Ubuntu 版)
     ......
  • C++ 元编程
    目录C++元编程1.术语2.元函数1.数值元函数示例:阶乘计算2.类型元函数示例:类型选择3.混合编程1.常规的计算点积范例2.混合元编程计算点积4.typelist实现设计和基本操作接口(算法)完整代码5.tuple实现基础知识1.左值、右值、左值引用、右值引用2.std::move......