Day 11 C++面向对象编程 之 对象模型+this指针+友元

一、C++对象模型和this指针
1. 成员变量和成员函数分开存储

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

C++编译器会给每个空对象分配一个字节的空间,是为了区分空对象占内存的位置,区分每一个空对象,每一个空对象也应该有一个独一无二的内存地址,进行区分。

若对象中有一个成员变量int,则一个对象占4个字节。只有非静态成员变量属于类的对象上,其他(静态成员变量、非静态成员函数、静态成员函数)都是独一份的,不属于类的对象上。

二、this指针的概念

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码。那么,这一块代码是如何区分是哪个对象调用的自己的呢?

c++通过提供特殊的对象指针,this指针,解决上述问题,this指针指向被调用的成员函数所属的对象(就是这个对象调用的它)。当对象p1调用这个函数,this就指向p1;对象p2调用这个函数,this就指向p2…

this指针是隐含在每一个非静态成员函数内的一种指针;this指针不需要定义,直接使用即可

1. this指针的用途:
  • 形参和成员变量同名时,可用this指针来区分;this指针指向的被调用成员函数所属的对象(调用这个函数的对象)
  • 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
	Person(int age)
	{
		//age = age;//命名相同,系统会认为三个是同一个东西,错误
		this->age = age;//命名相同,系统会认为三个是同一个东西,错误
	}
	void PersonAddAge(Person &p)
	{
		this->age += p.age;
	}
	int age;
};
//1.解决名称冲突
void test01()
{
	Person p1(7);
	cout << "p1的年龄:" << p1.age << endl;
}
//2.返回对象本身用*this
void test02()
{
	Person p1(6);
	Person p2(8);
	p2.PersonAddAge(p1);
	cout << "p2的年龄:" << p2.age << endl;
}

若这个函数本身返回的是一个void,所以不可以再调用成员函数,如果范围的是对象本身就可以,所以更改函数返回值类型

p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//错误

class Person
{
public:
    ...
     Person& PersonAddAge(Person &p)//最前面加了引用&,表示一直对其进行修改,若不加引用表示采用了拷贝构造函数
	{
		this->age += p.age;
		return *this;//*this指向的就是本体,返回的就是本体
	}
}
void test()
{
    Person p1(6);
	Person p2(8);
    //链式编程思想
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//正确
}

若不加引用&,表示是返回值的方式,会创建新的对象,即第一次调用结束后返回的就是一个拷贝后的p2’,而不是p2,第二次返回的是p2’',…

若加引用&,表示用引用的方式返回,不会创建新的对象,会一直返回原本的对象。

this指针的本质是指针常量,指针常量,他是一个“常量”,指向不可以改,指向的值可以修改

三、空指针访问成员函数

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

class Person
{
public:
	void showClassName()
	{
		cout << "this is a Person Class" << endl;
	}
	void showPersonAge()
	{
		if (this == NULL)
		{
			return;//判断一下
		}
		cout << "age=" << m_age << endl;
		//默认写成的是:cout << "age=" << this->m_age << endl;属性前面都会默认加this,表示是这个对象的属性
		//错误,但是我们并没有创建对象,只是创建了一个这个类型的指针,所以传入指针是NULL,没有对象就就更别说其中的成员,无中生有,所以有错
        //所以在前面要判断一下,增强代码的健壮性

	}
	int m_age;
};
void test01()
{
	Person *p=NULL;//创建一个指针,指向空
	p->showClassName();
	p->showPersonAge();
}

错误:但是我们并没有创建对象,只是创建了一个这个类型的指针,所以传入指针是NULL,没有对象就就更别说其中的成员,无中生有,所以有错,所以在前面要判断一下,增强代码的健壮性

四、const修饰成员函数
1. 常函数:
  • 成员函数后加const后,称这个函数为常函数;
  • 常函数内不可以修改成员属性(值);
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改
class Person
{
public:
	//this指针的本质是指针常量,指针常量,他是一个“常量”,指向不可以改,指向的值可以修改
	//Person *const this;这里可以修改值,但是不可以修改方向
	//const Person *const this;值也不可以修改了,所以C++给加在了函数头的后面,修饰的是this指针,让指针指向的值也不可以修改
	void showPerson() const
	{
		//m_a = 100;
		//this->m_a = 100;
		m_b = 200;
	}
	int m_a;
	mutable int m_b;//进行这种声明之后,就可以更改值了
};

函数后+const,实际修饰的是this指针,让指针指向的值也不可以修改

2. 常对象:
  • 声明对象前加const,称该对象为常对象;
  • 常对象只能调用常函数
void test01()
{
	const Person p;//常对象
	//p.m_a = 300;//错误,有const不可修改
	p.m_b=100;//正确,有mutable,可以修改
    //常对象只能调用常函数
    //p.func();//错误,不可调用普通成员函数,普通成员函数可以修改成员属性
    p.showPerson();//正确,常对象只能调用常函数
}

常对象不可调用普通成员函数,因为普通成员函数可以修改成员属性

五、友元

生活中,一个房子有客厅(public),有卧室(private)。客厅都可以访问,卧室只有自己可以访问,以及允许的对象可以访问。在程序中,有些私有属性,也想让类外特殊的一些函数或者类进行访问,友元就是解决此类问题的。

友元的目的技术让一个函数或者类访问另一个类中的私有成员。关键字为 friend有三种实现方式

  • 全局函数做友元、
  • 类做友元、
  • 成员函数做友元。
1. 全局函数做友元
class Building
{
	friend void goodGay(Building* building);//+了friend,表示此全局函数是好朋友,可以访问building中的私有成员
public:
	Building()
	{
		m_SittingRoom = "客厅";
		m_BathRoom = "卧室";
	}
	string m_SittingRoom;//客厅
private:
	string m_BathRoom;//卧室
};
//全局函数
void goodGay(Building *building)
{
	cout << "好朋友的全局函数正在访问:" << building->m_SittingRoom << endl;
	cout << "好朋友的全局函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
	Building building;
	goodGay(&building);
}

要在类中最前面进行声明,告诉编译器,这个函数是好朋友,可以访问这个类的对象中的私有成员

2. 类做友元
class Building
{
	friend class GoodGay;//GoodGay类是本类的好朋友,可以访问本类的私有成员
public:
	Building();
public:
	string m_SittingRoom;//客厅
private:
	string m_BathRoom;//卧室
};
//类外写成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BathRoom = "卧室";
}
class GoodGay
{
public:
	GoodGay();
	void visit();//参观函数,访问building中的属性
	Building* building;
};
GoodGay::GoodGay()
{
	building = new Building;//创建对象维护成员变量
}
void GoodGay::visit()
{
	cout << "好朋友的全局函数正在访问:" << building->m_SittingRoom << endl;
	cout << "好朋友的全局函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
	GoodGay gay;
	gay.visit();
}
3. 成员函数做友元
class Building;
class GoodGay
{
public:
	GoodGay();
	void visit();//参观函数,访问building中的属性
	void visit2();//不可以访问
	Building* building;
};
class Building
{
	friend void GoodGay::visit();//GoodGay类的这个成员函数是本类的好朋友,可以访问本类的私有成员
public:
	Building();//构造函数的声明
public:
	string m_SittingRoom;//客厅
private:
	string m_BathRoom;//卧室
};

//类外写成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BathRoom = "卧室";
}
GoodGay::GoodGay()
{
	building = new Building;
}
void GoodGay::visit()
{
	cout << "好朋友的visit函数正在访问:" << building->m_SittingRoom << endl;
	cout << "好朋友的visit函数正在访问:" << building->m_BathRoom << endl;
}

void GoodGay::visit2()
{
	cout << "好朋友2的visit2函数正在访问:" << building->m_SittingRoom << endl;
	//cout << "好朋友2的visit2函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
	GoodGay gay;
	gay.visit();
	gay.visit2();
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐