C++核心编程——4.3 C++对象模型和this指针

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

        在C++中,类内的成员变量和成员函数分开存储(虽然封装在一起,但是分开存储)

        只有非静态成员变量才属于类的对象上,静态成员(包括静态成员变量和静态成员函数)和非静态成员函数不属于类的对象上。

class Person {
public:
	Person() {
		mA = 0;
	}
	//非静态成员变量占对象空间(是属于类的对象上的,当类中只有一个int时,类就只占4个字节)
	int mA;
	//静态成员变量不占对象空间(不属于类的对象上,在类中不占用空间)
	static int mB; 
	//非静态成员函数也不占对象空间,所有函数共享一个函数实例(不属于类的对象上)
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
	}
};
int Person::mB = 10; //类内声明,类外初始化
int main() {

  //空对象(对象内为空)占用的内存空间为1个字节(用于区分两个对象,两个对象不能用一个空间)
  //C++编译器会为每一个对象也分配一个字节空间,是为了区分空对象占内存的位置
    
    
	cout << sizeof(Person) << endl; // 调用sizeof可以访问类的大小

	system("pause");

	return 0;
}

4.3.2 this指针概念

问题:

       C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例(所以对象都在调用这一个函数),也就是多个同类型的对象会共用一块代码,那这一块代码是如何区分那个对象调用自己的呢?

解决方案:

c++通过提供特殊的对象指针,this指针,解决上述问题。

特点:this指针指向被调用的成员函数所属的对象

隐含在每一个非静态成员函数内

不需要定义,直接使用即可

用途:

  • 解决名称冲突:当形参和成员变量同名时,可用this指针来区分

如下图所示,红色框和蓝色框中各属于一份数据,用this指针进行区分,其中蓝色框为传入的形参,红色框为类中的成员变量。

  • 返回对象本身:在类的非静态成员函数中返回对象本身,可使用return *this(通过解引用解开该对象)

传入的是一个类,用引用的方式返回

Person& PersonAddPerson(Person p)

链式编程示例:

        其中,p2.PersonAddPerson(p1)执行后返回p2(在语法上p2.PersonAddPerson(p1) =p2),返回p2后又嵌套后面的.PersonAddPerson(p1)组合成p2.PersonAddPerson(p1),执行后再返回p2,以此类推不断嵌套;

 传入的是一个类,用值的方式返回

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

 注:用值的方式返回,在执行完成员函数后会创建新的对象(属性和原对象一模一样,但是已经不是原对象),再在新对象上进行操作。

上图的执行逻辑:

           首先,执行p5.PersonAddPerson(p4):

                  先执行成员函数(只是执行到this->age += p.age,还没有到return语句),原来的p5对象上的age会加上p4对象上的age(此时对象p5上的age是p5.age+p4.age);

                再执行return语句,会创建出和p5一模一样的对象(对象上的属性一致)p5',再将p5'进行返回(此时返回的已经不是原来的p5,后续的操作都和p5没有关系);

         其次,执行p5'.PersonAddPerson(p3):

                 先执行成员函数,p5'上的age加上p3的age;

                再执行return语句,创建p5''返回;

         然后,执行p5''.PersonAddPerson(p2):

                先执行成员函数,p5''上的age加上p2的age;

                再执行return语句,创建p5'''返回;

          ......

           可以无线嵌套,但是已经和原始的p5没有关系,原始的p5只会加上p4的age而已,输出的p5.age的结果只会是原始的p5.age+p4.age

class Person
{
public:

	Person(int age)
	{
//1、当形参和成员变量同名时,可用this指针来区分
        //this指针指向被调用的成员函数所属的对象
        
	     this->age = age;
	}
    
//2、返回对象本身
	Person& PersonAddPerson(Person p)  //传入的是一个类,以引用的方式进行返回
   
	{
		this->age += p.age;  //自身的年龄加上传入的年龄
		//返回对象本身
	return *this;  //当调用p2时,this是指向p2的指针,如果返回指针的解引用就是p2的本体
        //this是指向p2的指针,*this指向p2的本体
	}
    
   Person PersonAddPerson1(Person p)   //返回值,会创建新的对象,再在新的对象上操作
        {
		this->age += p.age;  //自身的年龄加上传入的年龄
		//返回对象本身
	return *this;  //当调用p2时,this是指向p2的指针,如果返回指针的解引用就是p2的本体
        //this是指向p2的指针,*this指向p2的本体
	     }

	int age;
};

void test01()
{
	Person p1(10);  
    //此时在调用p1(被调用的),所以this指针指向的是p1(被调用的成员函数)
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
   
    //如果函数执行完之后能返回函数的本身,就可以进行一个嵌套
    //即p2.PersonAddPerson(p1)执行后返回p2(p2.PersonAddPerson(p1) 相当于p2)
    //链式编程思想,可以无限的往后延申(同cout无线延申输出)
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
    

    //把p1的age多加几次
	cout << "p2.age = " << p2.age << endl;
    
    Person p3(1);
    Person p4(2);
    Person p5(3);
    
    p5.PersonAddPerson1(p4).PersonAddPerson1(p3).PersonAddPerson1(p2);
    
    //每次执行会复制出一份一摸一样的,但是已经不是它本身
    
    cout << "p5.age = " << p5.age << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

4.3.3 空指针访问成员函数

C++允许空指针调用成员函数(只适用于成员函数中没有用到this指针)

如果用到this指针,需要加以判断保证代码的健壮性(判断指针为空则返回)

//空指针访问成员函数
class Person {
public:

	void ShowClassName() {
		cout << "Person类!" << endl;
	}

	void ShowPerson() {
		if (this == NULL) {
			return;
		}  //空指针直接返回就不会出错,提高健壮性(防止别人传空指针搞破坏)
		cout << mAge << endl;
 //实际上的表达是 cout << this-> mAge << endl 其中this本身为空,不可以访问里面的值
	}

public:
	int mAge;
};

void test01()
{
	Person * p = NULL;
	p->ShowClassName(); //空指针,可以调用成员函数(只适用于成员函数中没有用到this指针)
	p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}

int main() {

	test01();

	system("pause");

	return 0;
}

4.3.4 const修饰成员函数

常函数常对象
定义成员函数后加const声明对象前加const
语法

void 成员函数名() const

{}

const 类名 对象名
特点常函数内不可以修改成员属性(指针指向和指针指向的值)常对象只能调用常函数
成员变量普通成员变量可访问不可修改
mutable修饰的成员变量可访问可修改

const修饰成员函数的区别见下表所示:

const修饰不用const修饰
语法

void 成员函数名() const

{}

void 成员函数名() 

{}

类似指针语法const 类名 * const this类名 * const this

const即修饰类,又修饰指针

const只修饰指针
this指针的指向不可修改
this指针指向的值不可修改可修改
class Person {
public:
	Person() {
		m_A = 10;
		m_B = 20;
	}
     
	
	//如果想让指针指向的值也不可以修改,需要声明常函数
	void ShowPerson() const{
		

		//this指针(隐含在每一个非静态成员函数内)的本质是一个指针常量,指针的指向不可修改
		//this == NULL;  //报错,调用是this指针已经指向p,不可以再修改指向(NULL)
//普通成员函数可访问不可修改		
	//成员函数不加const:Person * const this       指针指向不可修改指向的值可以修改
		//m_A = 100;        //声明前面不加const则this指向的值可以修改
		//this->m_A = 100; //this指针隐含在每一个非静态成员函数内,逻辑上同上一句
   //成员函数加const:const Person * const this   指针指向和指向的值都不可以修改
		//m_A = 100;        //this指针指向的对象的数据是补可以修改的
		//this->m_A = 100; //同上
		cout << this->m_A << endl;

//mutable修饰的变量可修改可访问
		cout << this->m_B << endl;
		this->m_B = 100;
		cout << this->m_B << endl;
		cout << "访问常函数" << endl;

	}

	void MyFunc()  {
		m_A = 100;
	}

public:
	int m_A ;
    mutable int m_B; //可修改 可变的
};


//const修饰对象  常对象
void test01() {

	Person p;
	p.ShowPerson();

	const Person person; //常对象(对象前面加const)
//普通对象可访问不可修改
	//person.m_A = 100; //常对象不允许修改普通的成员变量:常对象不能修改成员变量的值(不允许修改指针指向的值)
	cout << person.m_A << endl;
//mutable对象可访问可修改
	cout << person.m_B << endl;
	person.m_B = 50; //但是常对象可以修改mutable修饰成员变量
	cout << person.m_B << endl;

	常对象只能访问常函数
	//person.MyFunc(); //常对象不能调用普通的成员函数函数,因为普通成员函数可以修改属性,而常对象本身不能修改属性,已经违背了
	person.ShowPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

  • 52
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值