NzN的C++之路--this指针

本文详细介绍了C++中this指针的作用、特性,以及它在类和对象操作中的关键角色,包括如何解决成员函数区分不同对象的问题,以及C++与C语言实现栈的对比,展示了C++封装的优势。
摘要由CSDN通过智能技术生成

        在已经入门了类和对象之后,今天我们来到了类和对象的part2部分--this指针。先三连后看是好习惯!!!

目录

一、this指针的引入

二、this指针的特性

三、this指针必会

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

1. C语言实现

2. C++实现


一、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;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1, d2;
	d1.Init(2024, 4, 6);
	d2.Init(2022, 4, 7);
	d1.Print();
	d2.Print();
	return 0;
}

        Date类中有 Init 与 Print 两个成员函数,成员函数都在公共代码区里,d1和d2调用的是同一个函数,那成员函数是怎么确定操作d1对象还是d2对象呢?

        C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。

        编译器处理之后,其实是把对象的地址传给了对应的函数。

        当成员函数Print( )通过this指针访问这些成员变量时,它实际上访问的是调用这个成员函数的实例化对象里的成员变量。每个对象的_year、_month和_day都存储在各自独立的内存区域中,这些内存区域是在对象被创建时随对象一起分配的。

注意:这些操作对用户是透明的,不需要用户来传递,编译器会自动完成。

二、this指针的特性

  • this指针的类型:类名* const,即成员函数中,不能给this指针赋值,但是this指向的内容可以被改变。
  • this指针只能在“成员函数”的内部使用
  • this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针。
  • this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。

三、this指针必会

1. this指针存在哪里?

        this指针存在栈上,因为this指针是一个形参。

        有时this指针也会存在寄存器上。因为this指针会频繁访问,放在寄存器上方便快速取用。

2. 下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行

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

        本道题答案选C。我们发现p是空指针,没有对空指针解引用,该语句符合语法规则,不会编译报错,因此A错误。我们通过空指针访问成员函数,可能认为这是错的就选了B。但实际上,尽管p被初始化为空指针,但Print( )函数只是打印一条消息,没有访问任何成员变量。如果调用成员函数没有实际依赖于this指针指向的对象,不需要访问通过this指针指示的内存地址,代码可以运行。因此,对于这种不访问任何成员变量的成员函数,通过空指针调用可以正常运行。

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

         为了更好地理解,我们在Print( )函数打印this指针的地址,打印结果为0。p是A类型的指针,他就会到A类型里找Print( )函数。Print( )函数要么传递对象的地址,要么传递指针。这里就是传递的p这个指针,传递一个空指针不会有问题。

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

        如果我们传递一个对象,虽然仍然是p->Print( ),但实际上传递的是a的地址,this指针这时候接收的是一个实例化对象的地址,这个地址不为空。

注意:上面代码中的p->Print( )中不管p是否为空指针,->都不会进行解引用。

3. 下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行

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

        本道题答案选B。p是空指针,空指针不会编译报错, A错误。因为这里p->Print( )给this指针传递的是p,this指针也为空。而访问Print( )函数中打印_a的值是通过this->_a来实现的。_a存在了this指针指向的空间上,但this指针此时为空,所以找不到_a,代码就会运行崩溃。

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

        在这种情况下,p->Print( )给this指针传递的是对象a的地址,此时this指针不为空,能在this指针指向的空间上找到_a,就可以正常运行。

总结:调用成员函数创建了对象就传递对象的地址,直接创建了指针就传递指针。空指针的this不能访问成员变量,非空指针的this允许在函数体中访问成员变量。

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

1. C语言实现

typedef int DataType;
typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;
void StackInit(Stack* ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 2);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}
	ps->capacity = 3;
	ps->size = 0;
}
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->array)
	{
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}
void CheckCapacity(Stack* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}
void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}
int StackEmpty(Stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}
void StackPop(Stack* ps)
{
	if (StackEmpty(ps))
		return;
	ps->size--;
}
DataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->size;
}

        在用C语言实现时,Stack相关操作函数有以下共性:

  • 每个函数的第一个参数都是Stack*
  • 函数中必须要对第一个参数检测,因为该参数可能会为NULL
  • 函数中都是通过Stack*参数操作栈的
  • 调用时必须传递Stack结构体变量的地址

        结构体中只能定义存放数据的结构,相关功能函数不能放在结构体中,数据和操作数据的方式是分离的,而且实现复杂,涉及到大量指针操作,容易出错。

2. C++实现

typedef int DataType;
class Stack
{
public:
	void Init()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 2);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(DataType data)
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	void Pop()
	{
		if (Empty())
			return;
		_size--;
	}
	DataType Top() { return _array[_size - 1]; }
	int Empty() { return 0 == _size; }
	int Size() { return _size; }
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

        C++中通过类可以将数据以及方法完美结合,通过访问权限可以控制那些方法在类外可以被调用(封装)。而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,即C++中 Stack *参数是编译器维护的,C语言中需用用户自己维护。

  • 38
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值