深入篇【C++】类与对象:const成员与Static成员


在这里插入图片描述

⏰<const成员>

🕓1.权限

在C语言中,我们知道const是用来缩小权限或者偏移权限的。

//权限的缩小和偏移
int main()
{
	const int a = 1;
	//const 修饰a表明a不能被修改
	//也就是不能通过其他方式来修改变量a,权限由原来的可读可写,变成了可读不可写,权限缩小了。

	int& b = a;//不能通过取别名来修改变量a
	int* c = &a;//也不能通过指针来修改变量a

	const int& b = a;//权限是可以偏移的,相同权限是可以访问的
}

在这里插入图片描述
在C++中如果const修饰一个对象会怎么样呢?

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 5, 16);
	d1.Print();
	const Date d2(2023, 5, 17);
	d2.Print();

}

在这里插入图片描述
而在这里我们发现d1可以调用成员函数Print,而d2无法调用成员函数Print,这是为什么呢?
这里的原因就在于const修饰了对象d2,使得d2的成员无法进行修改了。
我们知道调用成员函数时,会将对象的地址传给函数,函数会用一个this指针来接收。
在这里插入图片描述
而被const修饰的d2是不能再通过其他方式来对d2进行访问修改的。所以d2是无法调用Print成员函数的,所以只要cosnt修饰了对象,那么对象就无法再调用成员函数了,因为编译器会自动的将对象的地址传给成员函数,成员函数会用一个this指针来接收。
const使对象d2权限缩小了,由可读可写变成了只读。
那对象d2如何调用它的成员成员函数呢?
我们知道权限缩小后就无法调用,但权限偏移还是可以访问的。所以我们只要让成员函数的权限也变得跟对象d2一样大,那成员函数不就可以调用了吗。所以让const来修饰成员函数就能解决这个问题。

将const西洋参的成员函数称为const成员函数,const修饰类成员函数,实际上修饰的是该成员函数中隐藏的this指针,表明在该成员函数中不能对类的任何成员进行修改

用const修饰成员函数,成员函数的权限就和对象一样大了,那对象就可以调用成员函数了。
不过这里有一个问题,const修饰成员函数,其实是修饰隐藏的this指针,C++规定this指针不能显式出现在函数外和函数参数列表的。那const应该写在哪里呢?
C++要求const修饰成员函数,const一律写在函数后面。

void Print()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//其实本质就是下面这样:
	//只不过this指针不能显式出现
void Print(const Date* this)
	{
	    cout << _year << "-" << _month << "-" << _day << endl;
    }

在成员函数后面加上const后,普通对象和const修饰的对象都可以调用了

因为成员函数后面加上const后权限变小,普通对象肯定是可以调用的,因为普通对象权限相比较大,const修饰的对象就和const修饰的成员函数权限一样大,所以也可以调用。

那是不是所有的成员函数后面都能加上const修饰呢?
当然不是。

🕐2.规则

const成员规则:

要修改对象成员变量的函数后面是不能加const,一旦加上const那么该函数就不能修改对象成员变量了,但只要成员变量不需要修改的函数后面都能加上const。

所以我们对于那些不需要修改对象成员变量的函数后面都应该加上const,为什么呢?
因为这样普通对象和const修饰的对象都可以调用成员函数。

🕒3.思考:

思考下面几个问题:

1.const对象可以调用非const成员函数吗?

不能,const对象权限小,非const成员函数权限大。

2.非const对象可以调用const成员函数吗?

可以,非const对象权限大,const成员函数权限小。

3.const成员函数内可以调用其他的非const成员函数吗?

不能,cosnt成员函数权限小,其他非const成员函数权限大。

 4.非const成员函数内可以调用其他的const成员函数吗?

能,非const成员函数权限大,其他const成员函数权限小。

从权限角度我们可以很轻松的解决这些问题。

⏰<Static成员>

🕑1.概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量。用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。

有没有一个办法可以计算出一个程序中出现了多少个对象呢?
我们的初步想法是当对象实例化时肯定会调用构造函数,当拷贝生成对象时肯定会调用拷贝构造函数,当对象销毁时肯定会调用析构函数,所以我们只要在这些函数里弄一个计数器就就可以了,当对象调用构造函数或者拷贝构造函数时,计数器加一,当调用析构函数时计数器减一。
而这个计数器要保证在全局都有效,不能是局部变量,不然函数使用完计数器就销毁了,所以我们可以将计数的变量定义为全局变量count,最开始为0.

int _count=0;
class A
{
public:
	A() //构造函数
	{ ++_count; }
	A(const A & t)//拷贝构造 
	{ ++_count; }
	~A() //析构函数
	{ --_count; }

private:
	int a;
};

void TestA()
{

	A a1, a2;//定义了两个对象
	cout << _count << endl;//这里应该是2
	A a3(a1);//又拷贝一个对象,又多一个计数器应该是3
	cout << _count << endl;
	
}
int main()
{
	TestA();

}

在这里插入图片描述
这样就可以简单的计算出程序中一共创建了多少对象了。
不过上面的做法有些问题,有哪些问题呢?

计数变量作为全局变量可能会被修改。全局变量的缺点就是谁都可以访问,谁都可以修改。

有没有什么办法可以像C++一样将成员封装起来呢?不让别人随意访问这个变量呢?

C++设计出static成员来解决这个问题。
既想拥有全局范围,又不想被随意访问,只能使用static成员。
static修饰变量,使变量具有静态性,存放在静态区,生命周期跟全局变量一样,都是在程序结束后自动销毁。
而想让该变量不被随意访问,只能作为类成员变量,使用访问限定符private来限定,在类里成员函数是可以随意访问,但在类外无法随意使用。
其实本质上就是将全局变量/静态变量封装。
但C++中有规定:静态成员变量在类里声明,在类外定义。

class A
{
public:
	A()
	{
		++_count;
	}
	A(const A& t)
	{
		++_count;
	}
	~A()
	{
		--_count;
	}
	static int GetCount()
	{
		return _count;
	}
private:
	static int _count;//静态成员变量在类里面声明
};

int A::_count = 0;//在类外面定义

到这里你可能意识到不对劲了,那计数变量封装在类里面,我们如何得到它呢?

在类外我们是无法使用这个静态变量的。使用域作用符也没有用,域作用符只是告诉这个变量在哪里,但不给你访问能有是用呢。
而想得到这个成员变量,就要有this指针,但哪里来this指针呀。
所以C++又设计出static成员函数来解决这个问题。
用static修饰的成函数称为静态成员函数,而静态成员函数是没有this指针的,只要指定类域和访问限定符就可以访问。

class A
{
public:
	static int GetCount()//静态成员函数是没有this指针的,只要指定类域和访问限定符就可以调用
	{
		return _count;
	}
private:
	static int _count;
};
int A::_count = 0;

所以我们在类外就可以使用静态成员函数来获得这个成员变量,正常来说我们要想使用类里的成员函数,是需要有this指针的,但静态成员函数是没有this指针,所以它是可以被任意调用的(访问限定符是pubilc)。

class A
{
public:
	A()
	{
		++_count;
	}
	A(const A& t)
	{
		++_count;
	}
	~A()
	{
		--_count;
	}
	static int GetCount()
	{
		return _count;
	}
private:
	static int _count;
};

int A::_count = 0;
void TestA()
{

	A a1, a2;
	cout <<A::GetCount()<< endl;
	//指定类域和访问限定符就可以调用该函数
	A a3(a1);
	cout <<A::GetCount()<< endl;

}
int main()
{
	TestA();
	
}

在这里插入图片描述
所以一般来说,静态成员和静态成员函数是配套出现的,使用静态成员就要使用静态成员函数。

不过要注意静态成员是存储在静态区的,它不是存储在类里的。并且它不是某个对象特有的成员变量,它是所有对象共享的成员变量,每个对象对这个变量都可以修改,但不能控制其他对象对它修改,所有它是共用的。

这里总结一下静态成员和静态成员函数的特性

🕗2.特性

1.静态成员为所有类对象所共享,不属于某个具体的对象,它是存储在静态区的。
2.静态成员变量必须在类外定义,定义时不添加static关键字,在类里声明,还有静态成员变量不能给缺省值。
3.类静态成员可用 类名::静态成员或者对象.静态成员来访问。
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员。因为访问其他成需要this指针才可以。
5.静态成员也是类的成员,受访问限定符的限制。

🕕3.思考:

1.静态成员函数可以调用非静态成员函数吗?
不能!
因为如果非静态成员函数需要访问成员变量时,是需要this指针的,但静态成员函数没有this指针,所有无法调用。
在这里插入图片描述

2.非静态成员函数可以调用类的静态成员函数吗?

可以的。非静态成员函数有this指针,想干嘛干嘛。

3.如何设计一个类,只能在栈或者堆上创建对象?

int main()
{
	C c1;//栈上开辟的
	static C c2;//静态区开辟的
	C* c3 = new C;//堆上开辟的
}

创建对象都有一个特点那就是调用构造函数,所有我们首先将构造函数进行封闭,放进限定保护符里,不给它们调用那么就无法创建在栈上,在静态区,在堆上创建对象了,但我们要求是只在栈上或者堆上,不能全部无法创建呀。

在这里插入图片描述
我们可以这样做,利用成员函数来获得我们想要在哪开辟空间的功能。

class C
{
public:
	C Stack()
	{
		C c1;//栈上开辟的
		return c1;

	}
	C Static()
	{
		static C c2;//静态区开辟的
		return c2;
	}
	C* Pile()
	{
		C* c3 = new C;//堆上开辟的
		return c3;
	}
private:
	C()
	{ }
};

想在哪创建对象就调用哪个函数,这是我们的想法。
但问题是怎么调用呀?你无法调用成员函数呀。
要调用成员函数那必须要有this指针,也就是创建一个对象才可以调用成员函数,可我们要求就是能给定在哪开辟对象,这里却让我先创建一个对象然后再给定在哪创建对象?
这肯定是不行的,这里就需要用到刚刚的知识了,静态成员函数是没有this指针的,没有this指针也可以调用静态成员函数。
所以这里我们只要将这些成员函数变成静态成员函数,那么我们就可以调用啦。
这样问题就解决了。

class C
{
public:
	static C Stack()
	{
		C c1;//栈上开辟的
		return c1;

	}
	static C Static()
	{
		static C c2;//静态区开辟的
		return c2;
	}
	static C* Pile()
	{
		C* c3 = new C;//堆上开辟的
		return c3;
	}
private:
	C()
	{ }
};
int main()
{
	C c=C::Stack();//栈上开辟的
	C* c1 = C::Pile();//堆上开辟的
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
静态数据成员和静态成员函数的用法: 静态数据成员和静态成员函数一般用于实现与类相关的全局函数,如工具类函数、工厂函数等。静态数据成员和静态成员函数可以通过类名和作用域解析运算符::来访问。使用静态数据成员和静态成员函数可以提高程序的效率,减小程序的内存开销。但需要注意,静态数据成员和静态成员函数都不能访问非静态成员,因此需要根据具体情况选择是否使用。 const修饰对象和成员的概念用法: constC++中的关键字,它用于修饰对象和成员const修饰的对象和成员不能被修改,从而保证了程序的安全性和稳定性。 const修饰对象:const修饰的对象不能被修改,它的值在初始化之后就不能被改变。例如:const int a = 10;表示a是一个常量,它的值不能被修改。 const修饰成员const修饰的成员不能被修改,它在类中一般用于声明常量成员或常量成员函数。例如:const int MAX_SIZE = 100;表示MAX_SIZE是一个常量,它的值不能被修改。又例如:void print() const;表示print()函数是一个常量成员函数,它不能修改类的成员变量,只能读取成员变量的值。 使用const修饰对象和成员可以提高程序的安全性和可读性,减少程序的错误。但需要注意,const修饰的对象和成员在初始化后不能被修改,因此需要根据具体情况选择是否使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小陶来咯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值