类和对象-C++对象模型和this指针

  • 15.1 成员变量和成员函数分开存储
  • 15.2 this指针概念
  • 15.3 空指针访问成员函数
  • 15.4 const修饰成员函数

15.1 成员变量和成员函数分开存储

在C++中:

  • 类内的成员变量和成员函数分开存储。
  • 只有非静态成员变量才属于类的对象上。
  • 类对象的存储方式为字节对齐,总大小为类中最宽基本类型成员大小×非静态成员变量数量。

示例:

#include using namespace std; //成员变量和成员函数的分开 class Person {}; class Person_2 { 	int m_A;			   //非静态成员变量属于类的对象上 	static string m_Name;  //静态成员变量不属于类的对象上 	void func() {}		   //非静态函数函数不属于类的对象上 	static void func2() {} //静态函数函数不属于类的对象上 //只有非静态成员变量属于类的对象上	 }; class Person_3 { 	int m_A; 	double m_B; 	char m_C; }; string m_Name = "zzz"; void test1_01() { 	Person p; 	//空对象占用内存空间为:1  	//因为编译器会为每个空对象分配一个字节空间,仅仅用来区分不同空对象,使得不同空对象都有一个独一无二的内存位置。 	cout << "size of p = " << sizeof(p) << endl; } void test1_02() { 	Person_2 p; 	//非静态成员变量属于类的对象上,这里有有一个int,所以是4 	cout << "size of p = " << sizeof(p) << endl; } void test1_03() { 	Person_3 p; 	//字节对齐,和最大的对齐,double为8,所以为3*8 	cout << "size of p = " << sizeof(p) << endl; } int main() { 	test1_01(); 	test1_02(); 	test1_03();  	system("pause"); 	return 0; } 

15.2 this指针概念

我们知道在C++中成员变量和成员函数是分开存储的,

每一个非静态成员函数只会诞生一份函数实例, 也就是说多个同类型的对象(一个实例化的多个对象)会共用一块函数代码。

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

C++通过提供特殊的对象指针,this指针,解决上述问题——this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针,

this指针不需要定义,直接使用即可。

this指针的用途:

  • 当形参和成员变星同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用 return *this (*this就是解引用,将这个指针解引用,就得到对象本身)。注意要指明返回数据类型为这个类的引用的数据类型。

示例:

#include using namespace std;  class Person2 { public: 	Person2(int age) 	{ 		age = age; 	} 	int age; }; class Person2_02 { public: 	Person2_02(int age) 	{ 		this->age = age; 	}  	void PersonAddAge(Person2_02& p) 	{ 		this->age += p.age; 	} 	Person2_02 PersonAddAge2(Person2_02& p)   //因为返回类型是一个对象类型,如果直接用这个类的类型接收,会开辟新的内存来接收返回值 	{ 		this->age += p.age; 		cout << typeid(*this).name() << endl;   //可以看到(*this)的数据类型是class Person2_02 		return *this; 	} 	Person2_02& PersonAddAge3(Person2_02& p)   //因为返回类型是一个对象类型,用引用接收,可以将本体返回。 	{ 		this->age += p.age; 	 		return *this; 	} 	int age; };  void test2_01() { 	Person2 p(5); 	cout << "p的年龄为:" << p.age << endl; 	Person2_02 p1(5); 	cout << "p1的年龄为:" << p1.age << endl; 	Person2_02 p2(10); 	cout << "p2的年龄为:" << p2.age << endl; 	p2.PersonAddAge(p1); 	cout << "改变后p2的年龄为:" << p2.age << endl;  	//如果我想多次调用p2的函数,那么需要每次将对象返回。 	//链式编程思想 	p2.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1); 	//理论上应该是15+5+5+5 = 30,但是结果是15+5=20 	//因为每次都是另开辟内存来接收返回的对象,导致不能在原对象上做运算。  	cout << "再后来改变后p2的年龄为:" << p2.age << endl;  	p2.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1);//20+5+5+5 	cout << "最后改变后p2的年龄为:" << p2.age << endl; }  int main() { 	test2_01();  	system("pause"); 	return 0; } 

15.3 空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针 。

如果用到this指针,需要加以判断保证代码的健壮性。

#include using namespace std;  class Person3 { public: 	void showClassName() 	{ 		cout << "this is Person3 class" << endl; 	} 	void showPerson3Age() 	{ 		if (this == NULL) 		{ 			return; 		} 		//报错原因是因为传入的指针为NULL,   		cout << "age = " << m_Age << endl; //调用m_Age时,其实是这个代码:this->m_Age,现在this为空,所以报错。 	} 	int m_Age; };  void test3_01() { 	Person3* p = NULL; 	p->showClassName(); 	p->showPerson3Age(); }  int main() { 	test3_01();  	system("pause"); 	return 0; } 

15.4 const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数。
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改。

语法:

//常函数 返回值类型 函数名() const {}  //mutable mutable 数据类型 变量名; 

常对象:

  • 常对象的所有成员变量都不可修改。
  • 成员属性声明时加关键字mutable后,依然可以修改。
  • 声明对象前加const称该对象为常对象。
  • 常对象只能调用常函数
//常对象 const 对象名 对象; 

示例

#include using namespace std;  class Person4 { public: 	//this指针本质就是一个指针常量(指针的指向不可更改)  类似于Person* const this; 	//加上const后,就相当于const Person *const this,即指针指向的值也不可修改。 	void showPerson4() const 	{  		//this = NULL; this指针是不可修改的, 		//this->m_A = 100; 		this->m_B = 200; 	}  	void func() 	{ 		m_A = 100; 	}  	int m_A; 	mutable int m_B;  //变量加上关键字mutable后,就仍旧是可以更改的。 };  void test4_01() { 	Person4 p; 	p.showPerson4(); } void test4_02() { 	const Person4 p;  //变为常对象 	//p.m_A = 100; 	p.m_B = 200;      //m_B加了mutable,在常对象下也是可以修改的。 	 	//常对象只能调用常函数 	p.showPerson4(); 	//p.func();//非常函数不可调用 	//因为非常函数内可以修改成员变量,但是常对象已经被规定它的成员变量是不可修改的,所以会报错。 }  int main() { 	test4_01();  	system("pause"); 	return 0; }