this指针相关

一、类对象的存储方式

先说一下结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
注意空类没有成员变量的类的大小编译器给了这两种类一个字节来唯一标识这个类的对象。

this指针相关_第1张图片

class A
{
public:
	int _a;
	int _b;
};
class B
{
	// 空类
};
class C
{
public:
	void func()
	{
		cout << "func()" << endl;
	}
};
int main()
{
	cout << "A的大小:" << sizeof(A) << endl;
	cout << "B的大小:" << sizeof(B) << endl;
	cout << "C的大小:" << sizeof(C) << endl;
	return 0;
}

this指针相关_第2张图片

  • 为什么成员变量存放在在类对象中,成员函数不在类对象中?

成员变量存放在类对象中,每个类对象都会拥有自己的成员变量。这些成员变量占据了每个对象的内存空间,它们具有对象的特定值和状态。当创建多个对象时,每个对象都会有自己独立的成员变量,从而保证了数据的封装性。

相比之下,成员函数并不包含对象特定的数据,它们通常只需要访问类的成员变量以及执行一些操作。因此,将成员函数与对象实例分离可以节省内存空间,避免重复存储相同的函数代码。

另外,将成员函数与对象实例分离还能提高代码的复用性。多个对象可以共享同一个类的成员函数,而无需为每个对象都存储一份相同的函数代码。这样可以减少内存消耗,并且方便维护和修改代码。

总结起来,将成员变量存放在类对象中,而将成员函数与对象实例分离,能够节省内存空间、提高代码复用性,并符合面向对象编程的封装性原则。

补充:内存对齐的规则

1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8。
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。

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

二、this指针

  • 既然内存中不存放成员函数,那么程序又是怎么确定是哪个对象调用的函数呢?
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 2024, int month = 1, int day = 14)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	Date d1(2023, 12, 12);
	Date d2;
	d1.Print();
	d2.Print();
	return 0;
}

this指针相关_第3张图片

在C++中,每个成员函数都有一个隐含的额外参数,即指向调用该函数的对象的指针(this指针)。通过this指针,程序能够确定是哪个对象调用了该函数。

当调用一个成员函数时,编译器会自动将调用该函数的对象的地址作为this指针传递给函数。这样,在函数内部就可以通过this指针来访问对象的成员变量和其他成员函数。

所以上面代码就会被编译器处理成这样:

void Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

void Print(Date* this)
{
	cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
}

d1.Print();
d2.Print();

d1.Print(&d1);
d2.Print(&d2);
  • this指针的特性

1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。

2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。

总结:

1.this指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向正在被该成员函数操作的那个对象。(全局函数没有this指针
2.当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,由隐含使用this指针。
3.当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。
4.this指针被隐含地声明为:Class Name* const this,这意味着不能给this指针赋值;

在Class Name 类的const成员函数中,this指针的类型为:const className * const,这说明对this指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);
5.this并不是一个常规变量,而是个右值,所以不能取得this的地址(不能&this)。
6.在以下场景中,经常需要显式引用this指针
ⅰ.为实现对象的链式引用;
ⅱ.为避免对同一对象进行赋值操作
iⅱ.在实现一些数据结构时,如list。

  • this相关题目

1、下面描述错误的是( D )

A.this指针是非静态成员函数的隐含形参.

B.每个非静态的成员函数都有一个this指针.

C.this指针是存在对象里面的.

D.this指针可以为空

在C++中,this指针始终指向当前对象的地址,它是非静态成员函数的隐含形参。this指针不可能为空,因为它指向调用该成员函数的对象的地址。在非静态成员函数中,this指针可以用于访问该对象的成员变量和成员函数。

2、下列有关this指针使用方法的叙述正确的是( D )

A.保证基类保护成员在子类中可以被访问

B.保证基类私有成员在子类中可以被访问

C.保证基类公有成员在子类中可以被访问

D.保证每个对象拥有自己的数据成员,但共享处理这些数据的代码

this指针并不影响对基类成员的访问权限,它仅仅指向当前对象,无论是基类还是子类的成员。保护成员只能在派生类内部或友元函数中访问,私有成员只能在类内部访问,而公有成员可以在任何地方访问。this指针不会改变这些访问权限。

"共享处理这些数据的代码"指的是非静态成员函数中对对象的数据进行处理的代码段,这段代码可以被多个对象共享使用。换句话说,通过this指针,每个对象可以调用同一个非静态成员函数来处理自己的数据成员,这样就避免了为每个对象都复制一份相同的代码。

例如,假设有一个类Person,其中包含一个非静态成员函数printName()来打印该对象的姓名。当创建多个Person对象时,这些对象共享同一个printName()函数的代码,但通过各自的this指针,每个对象可以将自己的姓名传递给该函数进行打印,从而实现了数据的共享处理。

3、在C++函数中,有些函数是没有this指针的:

  1. 全局函数不具备this指针
  2. static函数不具备this指针
  3. 友元函数不具备this指针

空指针问题

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

1、

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

在执行 p->Print(); 这一语句时,其实执行的是A::Print(),因为p是一个类A的指针,所以编译器会在类A中查找是否存在Print()函数,发现类中存在这个函数则编译成功。

之后就进入运行阶段,虽然this指针为nullptr,但是Print()函数中没有执行对this解引用的操作,所以可以运行成功。

2、

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

同样的执行A::Print(),执行函数,在函数内部存在对this指针解引用的操作 this->_a ,所以运行崩溃。

this指针相关_第4张图片


今天的分享就到这里了,如果,你感觉这篇博客对你有帮助的话,就点个赞吧!感谢感谢……

你可能感兴趣的:(c++,开发语言)