C++拾遗--this指针

C++拾遗--this指针

前言

在用C++进行面向对象编程时,this指针是一个人尽皆知的东西。但我们真的清楚它吗?下面我们对它的存在意义和使用方法一一进行探讨。

this指针

存在意义:我们为何需要this指针?

看一个简单的例子

#include <iostream>
using namespace std;

class MyClass
{
protected:
	int a;
public:
	MyClass(int a) :a(a)
	{}
	void setA(int a)
	{
		this->a = a;
	}
	int getA() const
	{
		return a;
	}
};
int main()
{
	MyClass my1(0);
	MyClass my2(1);
	my1.setA(2);
	my2.setA(3);
	cout << "my1::a = " << my1.getA() << endl;
	cout << "my2::a = " << my2.getA() << endl;
	cin.get();
	return 0;
}

运行

这个例子实在是太简单了!大家用脚指头都能看明白。我们需要深入思考下:

1.setA方法中明明没有this形参,为何在方法体中可以使用this?

答:this形参是隐式定义的,它代表当前对象的内存地址。我们不写,并不代表它不存在。

2.在设计setA方法时,我故意把方法形参(int a)和类成员变量(int MyClass::a)设计成同名的。以至于function body中必须写成 this->a=a;才能完成赋值。若把形参写成不同名的,比如int A,是否可以省略this?

答:可以的。但本质上还是this->a=A;还是那句:我们不写,并不代表它不存在。编译器在进行编译时,会自动把“a=A”改为“this->a=A;”。

3.从以上两个问题还没弄清this的存在意义吗?那就多说一句,程序在内存中的存储原理:代码区是公共的,静态变量是公共的。公共是只有一份的意思。

前者是公共的理由是:节约内存。后者是公共的理由是:逻辑上本该如此!(静态变量的存在意义)

总之,类MyClass的任何对象在调用setA方法时,都是调用同一份代码。但为何会有不同的效果呢?对象my1在调用setA时,是对my1.a进行赋值。对象my2在调用setA时,是对my2.a赋值。它们执行的代码相同,却不会乱掉,就是因为this指针的存在!

这就完了吗?还没有,我们需要进一步指出:this指针隐式定义于方法形参中。那么它的位置是?这不好说,我的推测:就是第一个形参。我的理由:方法调用时,传入的实参是从右向左入栈的,出栈时,第一个位置的参数当然就最先出栈。(若有不同的看法,欢迎讨论!)以下的讨论就暂且基于这种假设。

所以,setA(int a);本质上应该是 setA(MyClass * const this, int a);

my1.setA(2);本质上应该是 my1.setA(&my1, 2);

再深入,细心的你会发现,我写的是 MyClass * const this,而不是 MyClass * this,多了一个const,为何?这当然也是有理由的!

理由很简单:若this不是const的,则可以随意更改this的指向,如下代码:

void setA(int a)
{
	this = &my;
	this->a = a;
}

当然,这样的代码肯定是通不过编译的。每次对setA的调用都变成了对my.a的赋值。

总结:默认情况下,this的类型是指向类类型非常量版本的常量指针。(《C++Primer》)

还没完,我们还得继续深入……

const成员函数是怎么一回事?它和this有联系吗?

我们知道,const成员函数是不可以对成员变量进行修改的。那它是如何做到的呢?

原因:const对成员函数的修饰,就是对this的修饰。

上面的例子中的int getA()const;本质上是 int getA(MyClass const * const this);

至此,真相终于大白。

时间: 2024-11-08 22:15:04

C++拾遗--this指针的相关文章

C++拾遗--智能指针

C++拾遗--智能指针 前言 内存泄露是常见的问题,新标准中的智能指针从根本上解决了这个问题.所谓的智能指针,其智能性体现在:当没有对象使用某块动态分配的内存时,那就自动释放这片内存. 智能指针 下面这段程序可耗尽内存,导致程序崩溃. #include <iostream> #include <Windows.h> using namespace std; int main() { while (1) { //每次动态分配80M内存 double *p = new double[1

C语言-拾遗(2) 指针 &amp;&amp; 声明

几个容易混淆的概念: 指针常量:即指针本身的值是不可改变的,而指针指向的变量的值是可以改变的; 常量指针:即指针指向的变量的值是不可改变的,而指针本身的值是可以改变的; 指向常量的指针 指针函数:函数,返回的是一个指针 函数指针:指针,指向一个函数(这里可以继续嵌套,指向的函数返回一个函数指针........) 数组指针:指针,指向数组 指针数组:数组内存放的是指针 指针与数组 1.:数组对应着一块内存区域(符号表中存在),而指针是指向一块内存区域.其地址和容量在生命期里不会改变,只有数组的内容

C++拾遗--类成员指针

C++拾遗--类成员指针 前言 类成员的类型与一般类型不同,那么类成员的指针类型自然与一般类型的指针有所区别.我们有必要来探讨下类成员指针的使用. 正文 类成员指针是指可以指向类的非静态成员的指针.它的类型包括了类类型和所指向的成员类型.一般而言,指针指向的是对象,而类成员指针指向的是类成员而非类对象. 需要指出,类成员指针不是可调用对象,要想通过类成员指针调用类成员,需结合类对象或类指针.静态类型成员属于类,类型与普通指针相似. 数据成员指针 一般的声明形式: 成员类型 classname::

C/C++拾遗(一):关于数组的指针和数组元素首地址的一道经典题

代码如下: #include <stdio.h> int main(void) { int a[5] = {1, 2, 3, 4, 5}; int *ptr = (int *)(&a+1); int *p1 = a; int *p2 = &a[0]; int *p3 = (int *)(&a); if(p1 == p2){ printf("p1 == p2\n"); }else{ printf("p1 != p2\n"); } i

C++拾遗--name_cast 显式类型转换

C++拾遗--name_cast 显式类型转换 前言 C++中提供了四种显式的类型转换方法:static_cast,const_cast,reinterpret_cast,dynamic_cast.下面分别看下它们的使用场景. 显式类型转换 1.staitc_cast 这是最常用的,一般都能使用,除了不能转换掉底层const属性. #include <iostream> using namespace std; int main() { cout << "static_c

C++拾遗--引用

C++拾遗--引用 前言 引用就是别名(alias).所谓别名,就是对已存在的对象另起一个名字.本身含义并不难理解,但与其它概念一组合,就成了使用难点.再加上新标准提出了新的一种引用-右值引用,引用这一概念就变得更加难以理解和使用. 正文 随着新标准(新标准往往就是新的技术)的提出,引用这一概念分成两类:左值引用.右值引用.其中左值引用是最常用的一种,而右值引用则是语言使用上的一种革新. 1.左值引用 左值引用的基本语法 Type &引用名 = 左值表达式; #include <iostre

C++拾遗--虚函数原理

C++拾遗--虚函数原理 前言 C++的多态依赖虚函数来实现.若类存在虚函数,则每一个类的实例都维护了一个地址,这个地址指向虚函数表.虚函数表中存放的是类中所有虚函数的地址.下面我们找出虚函数表的地址,从而获得每个虚函数的地址,然后使用地址直接调用虚函数. 正文 1.空类的size #include <iostream> using namespace std; class MyClass { }; int main() { cout << "sizeof(MyClass

c++基础知识拾遗

------------------<c++基础知识拾遗之&>--------------------------------- 在C里面,&有几种意思 1.当两个连在一起的时候.&&.代表的是"并且"的意思 例如a==1&&b==2.就是当a=1并且b=2的时候! 2.表示引用,例如int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名 3.&表示两种运算符,其中一种表示取值运算符,一

C++拾遗--构造函数(二)

C++拾遗--构造函数(二) 前言 在构造函数(一)中讨论的是默认构造函数的一些应用.这里我们来探讨下其它的一些有名构造函数,及其它们的应用场景. 实例 #include <iostream> using namespace std; class Complex { protected: int real = 0; int imag = 0; public: //默认构造 Complex() { cout << "Complex()" << endl