2021-11-08 C++封装继承多态---C++对象模型和this指针、友元、运算符重载(附代码理解)

3. C++对象模型和this指针

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

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

#include
using namespace std;
class person
{
	int m_a; //非静态成员变量  属于类的对象上的
	static int m_b;//静态成员变量    不属于类的对象上的
	void func() {} //非静态成员变量    不属于类的对象上的
	static void func2() {} 
};
int person::m_b = 10;
void test01()
{
	person p;
	//空对象占用的内存空间为:???  1个字节
	//c++ 编译器会给每个空对象也分配一个字节空间,为了区分对象占内存的位置
	//每个空对象也应该有一个独一无二的内存
	cout << "size of p=="<<sizeof(p) << endl;
}
void test02()
{
	person p;
	cout << "size of p==" << sizeof(p) << endl;
}
int main() {
	test02();
	
	system("pause");
	return 0;
}

3.2 this指针概念

我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码那么问题是:这—块代码是如何区分那个对象调用自己的呢?
C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的—种指针
this指针不需要定义,直接使用即可
this指针的用途:·当形参和成员变量同名时,可用this指针来区分 ·在类的非静态成员函数中返回对象本身,可使用return *this

#include
using namespace std;
//1 解决名称冲突

//2 返回对象本身用 return *this
class person
{
public:
	person(int age)
	{
		//this 指针指向的是 被调用的成员函数 所属于的对象
		this->age = age;
	}
	person &personaddage(person& p)
	{
		this->age += age;
		//this 指向p2的指针, *this 指向的就是p2的对象的本体
		return *this;
	}
	int age;
};
//1 解决名称冲突
void test01()
{
	person p1(18);
	cout << "p1的年龄" <<p1.age<< endl;
}
//2 返回对象本身用 return *this
void test02()
{
	person p1(18);
	person p2(18);
	//链式编程思想  &personaddage 为了套猴
	p2.personaddage(p1).personaddage(p1).personaddage(p1);
	cout << "p2的年龄" << p2.age << endl;
}
int main() {
	//test01();
	test02();
	
	system("pause");
	return 0;
}

3.3 空指针访问成员函数

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

#include
using namespace std;
//
class person
{
public:
	void showclassname()
	{
		cout << "this is person class" << endl;
	}
	void showpersonage()
	{
		//报错原因是因为传入的指针为空指针
		if (this == NULL)  //加上这个就可以了
		{
			return;
		}
		cout<<"age=" <<m_age<< endl;
	}
	int m_age;
};
void test01()
{
	person* p =NULL;
	p->showclassname();
	p->showpersonage();
}

int main() {
	test01();
	
	system("pause");
	return 0;
}

3.4 const修饰成员函数

常函数 常对象
成员函数后加const后我们称为这个函数为常函数 声明对象前加const称该对象为常对象
常函数内不可以修改成员属性 常对象只能调用常函数
成员属性声明时加关键字mutable后,在常函数中依然可以修改
#include
using namespace std;
//
class person
{
public:
	//常函数
	// this 指针本质为指针常量,指向不可以修改
	// const person * const this ; == this指针
	// 在成员函数后面加上const之后,修饰的是this的指向,让指针指向的值也不可以修改
	void showperson() const
	{
		//this 指针是  隐含在每一个非静态成员函数中的 一种指针
		//this->m_a = 100;
		//this = NULL;  //this 指针是不可以修改指针的指向的
		this->m_b = 100;
	}
	int m_a;
	mutable int m_b; //特殊变量,即使在常函数中,也可以修改这个值。加上关键字mutable
};

void test01()
{
	person p;
	p.showperson();
	cout << "good baby" << endl;
}
//常对象
void test02()
{
	const person p2; //在对象前加const,变成了常对象
	//p2.m_a = 100;
	p2.m_b = 100; // m_b是特殊值,在常对象里面也可以修改的
	//常对象只可以调用常函数
}
int main() {
	test01();
	system("pause");
	return 0;
}

4.友元

生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类访问另—个类中私有成员
友元的关键字为friend
友元的三种实现: .全局函数做友元 ·类做友元 ·成员函数做友元

4.1全局函数做友元

#include
using namespace std;
//建筑物的类
class building
{
	//goodgay 是building好朋友  可以访问私有成员
	friend void goodgay(building* building);
public:
	building()
	{
		m_sittingroom = "客厅";
		m_bedroom = "卧室";
	}
public:
	string m_sittingroom; //客厅
private:
	string m_bedroom; //卧室
};
//全局函数
void goodgay(building *b)
{
	cout <<"好基友全局函数 正在访问" <<b->m_sittingroom <<endl;
	cout << "好基友全局函数 正在访问" << b->m_bedroom << endl;  //私有怎么访问
}

void test01()
{
	building b2;
	goodgay(&b2);
}
int main() {
	test01();
	system("pause");
	return 0;
}

4.2类做友元

#include
using namespace std;
//建筑物的类
class Building;
class goodgay
{
public:
	goodgay();
	void visit(); //参观函数 访问building中的属性
	Building * building;
private:
};

class Building
{
	friend class goodgay;  // goodgay类是本类的好朋友,可以访问私有成员
public:
	Building();  //初始化
public:
	string m_sittingroom;
private:
	string m_bedroom;
};

//类外写成员函数
Building::Building()
{
	m_sittingroom = "客厅";
	m_bedroom = "卧室";
}
goodgay::goodgay()
{
	//创建建筑物的对象
	building = new Building;
}

void goodgay::visit()
{
	cout<<"好基友正在访问:" <<building->m_sittingroom << endl;
	cout << "好基友正在访问:" << building->m_bedroom << endl;
}
void test01()
{
	goodgay gg;
	gg.visit();
}
int main() {
	test01();
	system("pause");
	return 0;
}

4.3成员函数做友元

#include
using namespace std;
//
class Building;
class goodgay
{
public:
	goodgay();
	void visit(); //参观函数 访问building中的私有的成员
	void visit2(); //参观函数 不访问building中的私有的成员
	Building* building;
};
class Building
{
	//告诉编译器 goodgay类下的visit成员函数作为本类的好朋友,可以访问私有成员
	friend void goodgay::visit();
public:
	Building();
public:
	string m_sittingroom;
private:
	string m_bedroom;
};

//类外写成员函数
Building::Building()
{
	m_sittingroom = "客厅";
	m_bedroom = "卧室";
}
goodgay::goodgay()
{
	//创建建筑物的对象
	building = new Building;
}

void goodgay::visit()
{
	cout << "visit函数正在访问:" << building->m_sittingroom << endl;
	cout << "visit函数正在访问:" << building->m_bedroom << endl;
}
void goodgay::visit2()
{
	cout << "visit2函数正在访问:" << building->m_sittingroom << endl;
	//cout << "visit2函数正在访问:" << building->m_bedroom << endl;
}
void test01()
{
	goodgay gg;
	gg.visit();
	gg.visit2();
}
int main() {
	test01();
	system("pause");
	return 0;
}

5运算符重载

5.1加号运算符重载

三种重载方法!

#include
using namespace std;

class person
{
public:
	//1 成员函数重载+号
	person operator+(person & p)
	{
		person temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + p.m_b;
		return temp;
	}
	int m_a;
	int m_b;
};
//2 全局函数重载+号
person operator+(person& p1, person& p2)
{
	person temp;
	temp.m_a = p1.m_a + p2.m_a;
	temp.m_b = p1.m_b + p2.m_b;
	return temp;
}
//函数重载的版本
person operator+(person& p1, int num)
{
	person temp;
	temp.m_a = p1.m_a + num;
	temp.m_b = p1.m_b + num;
	return temp;
}
void test01()
{
	person p1;
	p1.m_a = 10;
	p1.m_b = 10;
	person p2;
	p2.m_a = 10;
	p2.m_b = 10;

	//成员函数重载本质
	person p3 = p1.operator+(p2);
	//person p3 = p1 + p2;

	//全局函数重载本质
	person p3 = operator+(p1, p2);
	//person p3 = p1 + p2;

	//运算符重载,也可以发生函数重载
	person p4 = p1 + 10; //person + int
	cout <<"p3.m_a" << p3.m_a  << endl;
	cout <<"p3.m_b" << p3.m_b << endl;

	cout << "p4.m_a" << p3.m_a << endl;
	cout << "p4.m_b" << p3.m_b << endl;
}

int main() {
	test01();
	system("pause");
	return 0;
}

5.2左移运算符重载

输出自定义数据类型

#include
using namespace std;

class person
{
	friend ostream& operator<<(ostream& cout, person p);
	friend void test01();
public:
	// 1 处成员函数重载  左移运算符  p.operator<<(cout)  简化版本 p<
	//不会利用成员函数重载<<运算符,因为无法实现 cout在左侧
	//void operator<<(person& p)
	//{
	//}
private:

	int m_a;
	int m_b;
};
//只能利用全局函数重载左移运算符
ostream & operator<<(ostream& cout, person p)   //本质 operator<<(cout,p) 简化 cout << p
{
	cout << "m_a = " << p.m_a << "m_b = " << p.m_b;
	return cout;
}

void test01()
{
	person p;
	p.m_a = 10;
	p.m_b = 10;
	//链式编程思想
	cout << p<<endl;
}

int main() {
	test01();
	system("pause");
	return 0;
}

5.3递增运算符重载

#include
using namespace std;
//重载++运算符
//前置和后置++运算符
class myinterger
{
	friend ostream& operator<<(ostream& cout, myinterger myint);  //好朋友友元
public:
	myinterger()
	{
		m_num = 0;
	}
	//重载前置++运算符  返回引用是为了一直对一个数据进行操作
	myinterger & operator++()
	{
		//先进行++的运算
		m_num++;
		//再将自身返回
		return *this;
	}
	//重载后置++运算符
	myinterger operator++(int)  //int让编译器知道你用的是后置运算符重载。
	//int 代表占位参数,可以用来区分前置和后置的递增
	{
		//先 记录当时的结果
		myinterger temp = *this;
		//后 递增
		m_num++;
		//最后将记录的结果进行返回的操作
		return temp;
	}
private:
	int m_num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, myinterger myint)   //本质 operator<<(cout,p) 简化 cout << p
{
	cout << myint.m_num;
	return cout;
}
void test01()
{
	myinterger myint;
	cout<<myint <<endl;
}

//重载后置++运算符
void test02()
{
	myinterger myint;
	cout << myint++ << endl;
	cout << myint << endl;
}
//前置递增返回引用,后置递增返回值
int main() {
	test02();
	system("pause");
	return 0;
}

5.4赋值运算符重载

#include
using namespace std;
//
class person
{
public:
	person(int age)
	{
		m_age = new int(age);
		cout << "取" << endl;
		cout << this->m_age << endl;
	}
	~person()
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
			cout <<"析构" << endl;
		}
	}

	//重载赋值运算符
	person & operator=(person& p)
	{
		//编译器提供浅拷贝
		//m_age = p.m_age;

		//应该先判断是否有属性在堆区,先释放干净在执行深拷贝
		cout << this->m_age << endl;
		if (m_age != NULL)      //将p2的的堆区数据清空,然后放入p1的数据!!!!!!!!!!!!!!!
		{
			delete m_age;
			m_age = NULL;
			cout << "释放" << endl;
			cout << this->m_age << endl;
		}
		//深拷贝
		m_age = new int(*p.m_age);
		//返回对象本体
		cout << this->m_age << endl;
		return *this;
	}
	
	int* m_age;
};

void test01()
{
	person p1(19);
	person p2(20);
	person p3(30);
	p3 = p2 = p1;
	cout << "p1的年龄为" << *p1.m_age << endl;
	cout << "p2的年龄为" << *p2.m_age << endl;
	cout << "p3的年龄为" << *p3.m_age << endl;
}
int main() {
	test01();
	system("pause");
	return 0;
}

5.5关系运算符重载

#include
using namespace std;
//
class person
{
public:
	person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}

	//重载 == 号
	bool operator==(person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return true;
		}
		return false;
	}
	//重载 != 号
	bool operator!=(person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return false;
		}
		return true;
	}
	string m_name;
	int m_age;
};
void test01()
{
	person p1("tom", 18);
	person p2("jack", 22);
	if (p1 == p2)
	{
		cout << "p1 和p2 是相等的" << endl;
	}
	else
	{
		cout <<"布线等" << endl;
	}
	if (p1 != p2)
	{
		cout << "p1 和p2 是相等的" << endl;
	}
	else
	{
		cout << "布线等" << endl;
	}
}
int main() {
	test01();
	system("pause");
	return 0;
}

5.6函数调用运算符重载

函数调用运算符()也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活

#include
using namespace std;
//打印输出类
class myprint
{
public:
	//重载函数调用运算符
	void operator()(string test)
	{
		cout << test<< endl;
	}
};

void myprint02(string test) {
	cout <<test  <<endl;
}

void test01()
{
	myprint myprint;
	myprint("hello world"); //由于使用起来非常类似于函数调用,因此称为仿函数
	myprint02("hello world");
}

//仿函数非常灵活,没有固定的写法  test01, test02
//加法类
class Myadd
{
public:
	int operator()(int num1, int num2)
	{
		return num1, num2;
	}
};
void test02()
{
	Myadd myadd;
	int ret = myadd(100, 100);
	cout <<"ret = " << ret<< endl;
	//匿名函数对象   运行玩一次就会被释放掉
	cout << Myadd()(100, 100) << endl;
}
int main() {
	test02();
	system("pause");
	return 0;
}

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