C++对象模型和this指针

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

在C++中,类内的成员变量和成员函数分开存储只有非静态成员变量才属于类的对象上

#include<iostream>
using namespace std;

class  Person
{
	int m_A;//非静态成员变量 属于类的对象上
	static int m_B;//静态成员变量 不属于类的对象上
	void func(){}//非静态成员函数 不属于类的对象上
	static void func2(){}//静态成员函数 不属于类的对象上
};

int Person::m_B = 100;

void test01()
{
	Person p;
	//空对象占用的内存空间为:1
	//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
	//每个空对象也应该有一个独一无二的的内存地址
	cout << "size of p=" << sizeof(p) << endl;
}

void test02()
{
	Person p;//只有一个int类型的非静态成员变量时:4
	cout << "size of p=" << sizeof(p) << endl;
}

void test03()
{
	Person p;//有一个int类型的非静态成员变量和一个静态成员变量时:4
	cout << "size of p=" << sizeof(p) << endl;
}

void test04()
{
	Person p;//有一个int类型的非静态成员变量和一个静态成员变量和一个非静态成员函数时:4
	cout << "size of p=" << sizeof(p) << endl;
}

void test05()
{
	Person p;//4
	cout << "size of p=" << sizeof(p) << endl;
}

int main()
{
	//test01();
	//test02();
	//test03();
	//test04();
	test05();
	system("pause");
	return 0;
}

第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP

this指针概念

C++通过提供特殊的对象指针,this指针。this指针指向被调用的成员函数所属对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可

this指针的用途:
1、当形参和成员变量同名时,可用this指针来区分
2、在类的非静态成员函数中返回对象本身,可以使用return *this;

#include<iostream>
using namespace std;
//1、解决名称冲突
class Person
{
public:
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		this->age = age;
	}
	int age;

	Person& PersonAddAge(Person &p)
	{
		this->age += p.age;
		return *this;
	}
};

void test01()
{
	Person p1(18);
	cout << "p1的年龄为:" << p1.age << endl;
}

//2、返回对象本身用*this

void test02()
{
	Person p1(10);
	Person p2(10);
	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	cout << "p2的年龄为:" << p2.age << endl;
}

int main()
{
	test01();
	test02();
	return 0;
}

第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP

自行探索上述返回&类型

#include<iostream>
using namespace std;

int& test02(int &num)
{
	num++;
	cout << "&num=" << &num << endl;
	return num;
}

int main()
{
	int b = 10;
	cout << "&b=" << &b << endl;
	cout << "&b=" << &(test02(b)) << endl;
	int* ret=&(test02(b));
	cout << "ret=" << ret << endl;
	return 0;
}

通过上述代码输出结果得出:
第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP
所有地址都是一样的。
但如果将int& test02(int &num)改成int test02(int &num)的话,代码会报错。因为这样函数返回的int值必须得新建一个int类型的变量来接收之后才可以取地址。
不过这里也能够确定返回不加引用的int的地址肯定不能是之前的地址。
那么问题来了,为什么将:

	Person& PersonAddAge(Person &p)
	{
		this->age += p.age;
		return *this;
	}

更改成

	Person PersonAddAge(Person &p)
	{
		this->age += p.age;
		return *this;
	}

之后不会报错呢?
只是这样更改后输出p2的年龄不是40而是20

先把代码改写成这样:
1、增加了几句输出
2、手动增加了拷贝构造函数

#include<iostream>
using namespace std;
//1、解决名称冲突
class Person
{
public:
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		cout << "initthis=" << this << endl;
		this->age = age;
	}
	int age;

	Person(const Person &p)
	{
		age = p.age;
		cout << "Person的拷贝数构造函数调用" << endl;
	}

	Person& PersonAddAge(Person &p)
	{
		cout << "addthis=" << this << endl;
		this->age += p.age;
		return *this;
	}
};

void test01()
{
	Person p1(18);
	cout << "p1的年龄为:" << p1.age << endl;
}

//2、返回对象本身用*this

void test02()
{
	Person p1(10);
	Person p2(10);
	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	cout << "p2的年龄为:" << p2.age << endl;
}

int main()
{
	//test01();
	test02();
	return 0;
}

输出得到:
第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP
可见,p2的this一直是0075FC1C
但是如果把Person& PersonAddAge(Person &p)改成Person PersonAddAge(Person &p)
输出如下:
第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP
在其返回Person类型的时候,调用了拷贝构造函数,相当于下一次进行该函数的时候已经是另一个对象了。
我们可以再添加一些输出来观察this->age的变化。

	Person PersonAddAge(Person &p)
	{
		cout << "addthis=" << this << endl;
		cout << "this->ageb=" << this->age << endl;
		this->age += p.age;
		cout << "this->agea=" << this->age << endl;
		return *this;
	}

第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)-LMLPHP
可以看到,虽然this->age的值最终达到了40,但是由于最终的输出结果是p2.age,而p2的地址是00CFF9B0。因此p2.age=20。

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性

#include<iostream>
using namespace std;

class Person
{
public:
	void showClassName()
	{
		cout << "this is Person class" << endl;
	}
	void showPersonAge()
	{
		//报错的原因是因为传入的指针是NULL
		//if (this == NULL)
		//{
		//	return;
		//}//建议加上这几行代码加以保护
		cout << "age=" << m_Age << endl;
	}
	int m_Age;
};

void test01()
{
	Person* p = NULL;
	p->showClassName();
	//p->showPersonAge();//会崩
}

int main()
{
	test01();
	return 0;
}

const修饰成员函数

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

常对象:
1、声明对象前加const称该对象为常对象
2、常对象只能调用常函数

#include<iostream>
using namespace std;

class Person
{
public:
	//this指针的本质是常量指针 指针的指向是不可用修改的
	//const Person* const this
	//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
	void showPerson()const
	{
		//m_A = 100;//报错
		//this=NULL;//this指针不可以修改指针的指向
		this->m_B;//加了关键字mutable就可以修改
	}
	void func()
	{

	}

	int m_A;
	mutable int m_B;
};

void test01()
{
	Person p;
	p.showPerson;
}



void test02()
{
	const Person p;//在对象前加const,变为常对象
	//p.m_A = 10;//报错
	p.m_B = 100;//可修改,在常对象下也可以修改
	p.showPerson();
	//p.func();//报错 常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性


	//常对象只能调用常函数
}

int main()
{
	test01(); 
	test02();
	return 0;
}
07-03 11:06