04、指针引用_类基础

本文介绍了C++中的指针、引用和类的基础知识,强调指针作为内存地址的整数表示,以及引用作为更安全的指针替代品。同时探讨了类的使用,包括数据封装和方法。此外,还讲解了`static`关键字在类内外的不同含义,以及如何在全局范围内限制其可见性和作用域。文章通过示例代码加深了对这些概念的理解。
摘要由CSDN通过智能技术生成

指针

内存是计算机的一切

指针对管理和操纵那个内存十分重要

什么是指针?

指针是一个整数,一个数字,它存储一个内存地址。它只是一个在内存中存放地址的整数,如果我们给一个指针一个类型,只是表明在那个地址的数据可能是我们给它的类型,我们写出来,可以让我们从语法上来说更容易理解。总之,一个类型,不会改变一个指针,指针永远都只是一个整数,一个内存地址。

类型对于操作这块内存有用,如果我想读取或者写入它,内存类型可以帮助我,因为编译器会知道。比如,一个int型是4字节,所以当我们要设置一个值的时候,它会设置4个字节内存。

总结:指针只是一个存储着内存地址的整数

#include<iostream>

using namespace std;

int main()
{
	int* ptr = nullptr;
	int val = 8;
	ptr = &val;
	//逆向引用指针,现在可以访问指针中存的那块内存中的数据,可以向那块地址读取或者写入数据
	*ptr = 1;

	//分配8字节的内存,并返回一个指向这块内存的开始地址的指针
	char* buffer = new char[8];	//new 关键字,在 堆 中请求内存
	char** bufptr = &buffer;	//实际上,指针也是一个整数
	memset(buffer,0,8);	//用我们指定的数据填充一块内存
	delete[] buffer;
	return 0;
}

引用(Reference)

引用只是指针的一个扩展。

引用必须引用一个已经存在的变量,引用本身并不是一个新的变量,它并不真正占用内存。而指针是一个实实在在的整型变量。

没有任何事情是引用能做但指针不能做的,用好引用可以让代码变得干净和简单很多,更有可读性,不会像指针那样让程序很难懂。

一旦你声明了一个引用,你就不能更改它所引用的对象 \textcolor{red}{一旦你声明了一个引用,你就不能更改它所引用的对象} 一旦你声明了一个引用,你就不能更改它所引用的对象

而我们声明了指针后,指针是可以改变指向的对象的。

在声明引用时,必须初始化,不能只声明不赋值。

#include<iostream>
#define LOG(x) cout << x << endl
using namespace std;

void Increment1(int value)
{
	value++;
}

void Increment2(int* value)
{
	(*value)++;
}

void Increment3(int& value)
{
	value++;
}
int main()
{
	int a = 5;
	int b = 6;
	/*
	 传值调用,在调用该函数时,程序将会拷贝参数值 5 到这个函数里,直接拷贝
	 这将会创造一个全新的变量value,调用完后会发现,这里a没有增加,只是value的值增加了
	*/
	Increment1(a);	
	LOG(a);

	/*
	 直接将需要增加的变量的地址传入函数
	*/
	Increment2(&a); 
	LOG(a);

	/*
	 调用此函数时,会声明一个引用,即等价于:
	 int& value = a;
	 value++;
	*/
	Increment3(a);
	LOG(a);

	int& ref = a;		//任何情况下,ref就是a,我们只是给a创建了一个别名
	/*
	 一旦你声明了一个引用,你就不能更改它所引用的对象。下面这种做法的最终结果:
	 a = 6;
	 故,因为这种特性,在声明引用时,必须初始化,不能只声明不赋值
	*/
	ref = b;	

	//int& ref2;		//错误
	LOG(ref);

	return 0;
}

小结:指针和引用的区别

  • 引用被创建的同时必须被初始化,而指针可以在创建后的任何时候初始化
  • 不能有NULL引用,引用必须与合法的存储单元关联。而指针是可以为NULL
  • 一旦引用被初始化,就不能改变引用的关系。而指针则可以随时改变所指的对象

类基础(class)

事实上,C++支持,面向过程、基于对象、面向对象、泛型编程 四种编程风格。

简而言之,类 是一种将数据和函数组织在一起的方式。类基本上就是我们创建的一个新的变量类型

例子:在制作游戏时,常常需要创建一个玩家角色,此角色有位置(x,y)、速度,如果我们直接用变量来存储这些数据,当有1个甚至几千个角色时,创建起来就会很困难、很乱了,这样会产生很多代码,并且难以维护下去。此时,我们可以用类来简化这一步,我们可以为玩家创建一个类,叫Player,一次性包含所有的数据,最终作为一个类型。

访问控制:当你创建一个类时,你可以指定类中成员的可见性,默认情况下类中的成员访问控制都是私有的, 意味着只有类内部的函数才能访问这些变量 \textcolor{red}{意味着只有类内部的函数才能访问这些变量} 意味着只有类内部的函数才能访问这些变量

类中的函数称作 方法

小结:本质上将,类就是能使我们能对变量进行组织,变成一个类型,还为这些变量添加了函数

#include<iostream>
#define LOG(x) cout << x << endl
using namespace std;

class Player {
public:
	int x, y;
	int speed;
	void Move(int xa, int ya)
	{
		x += xa * speed;
		y += ya * speed;
	}
};

/*
我们可以将函数,移到类中,这样可以不用传入player对象了,每个对象都会拥有自己的Move函数
当我们为特定的类调用Move函数时,就是调用他自己的,这和类外的没有什么区别,但他确实让代码
看起来简洁了不少,且易于维护
*/
void Move(Player& player,int xa,int ya)
{
	player.x += xa * player.speed;
	player.y += ya * player.speed;
}

int main()
{
	Player player1;	//实例化一个 Player 对象
	player1.x = 0;
	player1.y = 0;
	player1.Move(1, 1);
	

	return 0;
}

class VS struct in C++

C++中结构体和类有什么区别?什么时候该用结构体?什么时候又该用类?

答:基本没有区别。只是类中的成员默认情况下是私有的,而结构体中的成员默认情况下是公有的(这是唯一的区别)。

结构体在C++中存在的唯一原因就是它想要维持与C之间的兼容性,因为C中没有类。

编程风格:

  • 当我们谈论 plain old data或者只是包含一些变量的结构时,建议用struct。如:数学中的向量类
  • 结构体尽量就是数据的结构体
  • 如果想要有很多的功能,像游戏里的一个玩家,或者其他可能继承的东西,建议使用class

How to writer a C++ classs

日志管理信息系统(简易版)

功能:

  • 向控制台发送文本
  • 控制我们发送给控制台的日志信息等级(error , warning , message)
  • 如果是message等级,则三种信息都要打印;如果是warning等级,则只打印error和warning信息;如果是error等级,则只打印error信息
#include<iostream>

using namespace std;

class Log
{
	/*
	* 使用两部分public,将公有方法和公有成员变量分开
	*/
public:
	const int LogLevelError = 0;
	const int LogLevelWarning = 1;
	const int LogLevelInfo = 2;
private:
	/*
	 * 用一个 m 的前缀,代表这是一个私有成员变量
	 * 在后面的使用过程中可以很清晰的知道那些是成员变量,那些是局部变量
	 * 这是良好的编程习惯
	*/
	int m_LogLevel = LogLevelInfo;		
public:
	void SetLevel(int level)
	{
		m_LogLevel = level;
	}

	void Error(const char* message)
	{
		if (m_LogLevel >= LogLevelError)
			cout << "[ERROR]: " << message << endl;
	}

	void Warn(const char* message)
	{
		if(m_LogLevel >= LogLevelWarning)
			cout << "[WARNING]: " << message << endl;
	}

	void Info(const char* message)
	{
		if (m_LogLevel >= LogLevelInfo)
			cout << "[INFO]: " << message << endl;
	}
};

int main()
{
	Log log;
	log.SetLevel(log.LogLevelWarning);
	log.Error("Hello!");
	log.Warn("Hello!");
	log.Info("Hello!");
	cin.get();
}

static 关键字

C++中的static根据上下文有两种意思:

  • 在类class或者结构体struct外使用static
  • 在类class或者结构体struct里使用static

类或者结构体外的static修饰的符号在链接阶段是局部的,也就是它只对定义它的编译单元(.obj)可见。而类或者结构体里的static表示这部分内存是这个类的所有实例共享的,简单来说,就算实例化了很多次这个类或者结构体,但那个静态变量只会有一个实例,类里面的静态方法也是,静态方法里没有该实例的指针(this)

本小节仅研究类或者结构体外的static修饰符

理解“类或者结构体外的static修饰的符号在链接阶段是局部的,也就是它只对定义它的编译单元(.obj)可见”

实例:

情形一,有两个cpp文件:

//main.cpp
#include<iostream>

using namespace std;

int s_Variable = 10;

int main()
{
	cout << s_Variable << endl;
	cin.get();
}

//Static.cpp
//static 表示在链接的时候只在这个编译单元(.obj)可见
static int s_Variable = 5;		//加上s前缀表明是 静态变量

可以编译通过。这有点像 class 里面的private成员,其他的编译单元不能访问s_Variable,链接器在全局作用域下找不到它。

情形二,同样两个cpp文件,去掉Static.cpp里面的static修饰符,会报链接错误:
在这里插入图片描述

两个全局变量的名字是不能一样的,一种解决方法是把main.cpp中的s_Variable变量变成另一个的引用(去掉赋值,在前面加上extern关键字)

//main.cpp
#include<iostream>

using namespace std;

extern int s_Variable;//这样将在另外的编译单元中找到s_Variable的定义

int main()
{
	cout << s_Variable << endl;
	cin.get();
}

//Static.cpp

int s_Variable = 5;	

函数用static修饰,是同样的效果。

类class或者结构体struct外,static 的全部含义:当你在class或者struct外定义一个静态变量(函数),这意味着 你定义的变量(函数)是只对声明它的cpp文件(编译单元)可见的。

重点是:尽量让全局函数和变量都用static标记,除非它们必须要用在其他的编译单元里

static for class and struct

如果你在内部定义一个static变量,这意味着这个类的所有实例中,这个变量只有一个实例。比如:创建一个Entity类,然后我不断创建这个类的实例,但是那个static变量仍然只会有一个,这意味着如果一个实例改变了这个static变量,这个改变会体现在所有的类实例中,就像是这个类的全局实例,静态方法也是一样,静态方法不通过类的实例就可以调用(使用类名加范围解析符号::就可以访问),静态方法不能访问非静态变量
在这里插入图片描述

#include<iostream>

using namespace std;

struct Entity
{
	 /*
	  当我们把x,y设置为静态变量时,我们就把这两个变量变成在所有Entity类的实例中只有一个副本
	  不同实例的x,y变量指向的是同一个共享空间
	  当我们需要将一条信息在所有实例中共享时,这是有意义的
	 */
	 static int x, y;

	static void print()
	{
		cout << x << "," << y << endl;
	}
};
//类的非静态方法在编译时的真正样子
static void print(Entity e)
{
	cout << e.x << "," << e.y << endl;
}
//******************************


int Entity::x;
int Entity::y;

int main()
{
	Entity e;
	Entity::x = 2;
	Entity::y = 3;

	Entity e1;
	//这样引用变量其实是没有意义的
	/*e1.x = 5;
	e1.y = 8;*/

	//可以像这样引用,就像是在名叫Entity的命名空间里创建了两个变量,它们实际上不属于类了
	//当我们新建类的实例的时候,它们并不会重新分配内存
	Entity::x = 5;
	Entity::y = 8;

	Entity::print();
	Entity::print();
	/*
     如果我们将xy改为非静态变量,print函数仍然是静态的,这时调用函数便会报错
	 因为静态方法是不能访问非静态变量的,原因就是 静态方法是没有实例的
	 本质上,类里的每一个非静态方法都会获得当前实例作为参数(this指针),而静态方法是没有这个隐藏参数的
	 静态方法和我们在类的外部编写的方法是一样的
	*/
	

	cin.get();
	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值