类和对象下

标题一· 取地址运算符重载

1·const成员函数

我们先来看一段代码

class DATA
{
public:
	DATA()
	{
		int * arr = (int *)malloc(sizeof(int) * 4);
		if (arr == nullptr)
		{
			perror("malloc fail");
			exit(1);
		}
		_Arr = arr;
		_capacity = 4;
		_size = 0;
	}
	void Print()const
	{
		std::cout << "print()" << std::endl;
	}
	/*本意上等价于void Print(const DATA * const this)
	{
		std::cout << "print()" << std::endl;
	}*/
	/*===========================================================*/
	void Push(int value)const
	{
		_Arr[_size] = value;
		_size++;//这里会报错
	}
	/*等价于void Push(const DATA * const this,int value)
	{
		this->_Arr[this->_size] = value;
		this->_size++;这里我们可以看到this指针是const类型的,不允许改变值,所以会报错
	}*/
private:
	int _capacity;
	int _size;
	int* _Arr;
};

int main()
{
	return 0;
}

在这里插入图片描述

将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后
⾯。
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改,const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this

实际用得也挺多,它有利于我们代码的自查,不需要参数修改的函数加上它,当我们实际写代码时,改变了他的参数,那么他就会及时的报错(目前只知道这个用法),而需要参数改变的不加就行了

二· 取地址运算符重载

取地址运算符重载,一般不用我们自己写,用默认的重载函数就可以了,但是如果你不想别人取到你的类的地址也可以自己定义

{
public:
	DATA(int year = 2024,int mounth = 9,int day = 6)
	{
		_year = year;
		_mounth = mounth;
		_day = day;
	}
private:
	int _year;
	int _mounth;
	int _day;
};

int main()
{
	DATA A;
	DATA* T = &A;
	return 0;
}

在这里插入图片描述

这里我们可以看到当我们用默认的取地址运算符重载时他是能够正常访问我们类的地址的,下面我们来看若我们想让别人看到不对的地址该怎么写

class DATA
{
public:
	DATA(int year = 2024,int mounth = 9,int day = 6)
	{
		_year = year;
		_mounth = mounth;
		_day = day;
	}
	DATA* operator&()
	{
		return (DATA*)0x80FFA4;
	}
	const DATA* operator&()const
	{
		return (DATA*)0x80FFA4;
	}
private:
	int _year;
	int _mounth;
	int _day;
};

int main()
{
	DATA A;
	DATA* T = &A;
	return 0;
}

在这里插入图片描述
这里我们就能明显看到我们访问到类的地址是我们想让别人反问的地址,而不是类的真实地址

三 ·初始化列表

前面我们介绍了构造函数,在函数体内进行初始化,其实还可以在初始化列表里进行初始化
,初始化列表的使用方式是在函数()后跟一对冒号,然后是初始化的成员,成员后跟一对圆括号,用于初始化变量,可以是值,可以是表达式,成员之间用逗号隔开。
例如:

class DATA
{
public:
	DATA(int year = 2024, int mounth = 9, int day = 6)
		:_year(2024),
		_mounth(10),
		_day(9)
	{}
private:
	int _year;
	int _mounth;
	int _day;
};

int main()
{
	DATA A;
	return 0;
}

下面是调试代码
在这里插入图片描述
可以看到A的成员变量已经初始化成了我们想要的值了
每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地⽅。
**注意:**一般有几种情况必须要使用初始化列表来初始化成员变量
(1)静态成员变量(const)

在这里插入图片描述
可以看到这里_a成员变量报错了,但如果我们放到初始化列表部分则不会报错

(2)引用成员变量
在这里插入图片描述
接下来看正确的代码

在这里插入图片描述

四 ·类型转换

C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
构造函数前⾯加explicit就不再⽀持隐式类型转换
我们举以下一个样例
在这里插入图片描述
可以看到当int类型拷贝给自定义类型时是会报错的,正确的做法是在构造函数时要有内置类型的形参,他才可以进行隐式类型转换
在这里插入图片描述
这样他就不会报错了
当我们加上explicit时他就不支持隐式类型转换了
在这里插入图片描述

五 ·static修饰

• ⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。
• 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
• ⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
• 静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针。
• ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
• 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量
和静态成员函数。• 静态成员也是类的成员,受public、protected、private访问限定符的限制。
• 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表。

class DATA
{
public:
	DATA(int year = 2024,int mounth = 9,int day = 20)
	{
		_year = year;
		_mounth = mounth;
		_day = day;
		_a = 40;//在非静态的函数里可以访问静态成员(类似于权限缩小)
	}
	static void PRINT()
	{
		_a = 30;
//		_year = 2025;在静态函数里不能访问非静态成员(类似于权限放大)
	}
	
private:
	int _year;
	int _mounth;
	int _day;
	static int _a;//声明在类域内
};

int DATA::_a = 20;//定义在类域外

int main()
{
	DATA A;
	return 0;
}

六 ·友元

• 友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类
声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。
• 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
• 友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。
• ⼀个函数可以是多个类的友元函数。
• 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
• 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
• 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是B的友元。
• 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。
(前面类(中)已有代码演示,这里就不在演示了)

七 ·内部类

• 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
• 内部类默认是外部类的友元类。
• 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地⽅都⽤不了。

#include<iostream>
using namespace std;
class A
{
private:
	static int _k;
	int _h = 1;
public:
	class B // B默认就是A的友元
	{
	public:
		 void foo(const A & a)
		{
			cout << _k << endl; 
			cout << a._h << endl; //允许访问A的私有成员
		}
	};
};
int A::_k = 1;


int main()
{
	cout << sizeof(A) << endl;
	A::B b;
	A aa;
	b.foo(aa);
	return 0;
}

八 ·匿名对象

⽤类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名(实参)定义出来的叫有名对象
• 匿名对象⽣命周期只在当前一行,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

class Solution {
public:
 int Sum_Solution(int n) {
 //...
 return n;
 }
};
int main()
{
 A aa1;
 // 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义
 //A aa1();
 // 但是我们可以这么定义匿名对象,匿名对象的特点不⽤取名字,
 // 但是他的⽣命周期只有这⼀⾏,我们可以看到下⼀⾏他就会⾃动调⽤析构函数
 A();
 A(1);
 A aa2(2);
 // 匿名对象在这样场景下就很好⽤,当然还有⼀些其他使⽤场景,这个我们以后遇到了再说
 Solution().Sum_Solution(10);
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值