C++ | 类和对象(下)(static成员、友元、内部类、匿名对象)

发布于:2024-08-08 ⋅ 阅读:(86) ⋅ 点赞:(0)

目录

​编辑

static成员

static性质简介

static属于整个类,属于所有对象

static成员的声明与定义

static函数

友元friend

友元特性简介

友元关系讲解

内部类

特性一

特性二

匿名对象

结语


static成员

static性质简介

static成员在类里面是非常独特的,因为这个成员并不存在于类里面,就像是函数一样

我们先来看这么一段程序:

class A
{
public:
	int _a;
	static int _b;
};

int main()
{
	A aa1;
	cout << sizeof(aa1) << endl;
	return 0;
}

我们能看到,这段程序的目的就是看这个类的大小,我们的类里面有一个普通的int成员和一个static成员,但是结果为 4,这说明static成员是不存在于类中的

static属于整个类,属于所有对象

这句话的意思就是,我们无论创建了多少个对象,所有的对象想要用这个static成员,那么使用的就都是同一个static成员,因为这个static成员属于所有对象

我们可以验证一下这个特性,来看这么一段代码:

ps:该段代码中有关于static成员声明与定义的问题和static成员函数的问题,稍后会讲到

class A
{
public:
	A()
	{
		_b++;
	}

	~A()
	{
		_b--;
	}

    //静态成员函数
	static int get_b()
	{
		return _b;
	}

private:
	int _a;
	static int _b;//声明
};

int A::_b = 0;//定义

int main()
{
	A aa1;
	A aa2;
	A aa3;
	A aa4;
	cout << A::get_b() << endl;
	return 0;
}

我们能看到,每个对象创建出来时,都会调用对应的构造函数,而我们在构造函数里面就干一件事:将静态成员++

又因为所有对象共用同一个static成员,所以我们每创建一个对象,我们的_b就会++一下

最后,我们再通过静态成员函数将静态成员变量取出,就能看到具体此时有几个对象还存在了

static成员的声明与定义

因为是static成员,所以是不能在构造函数中进行初始化的

那我们可以按如下方式赋值吗?

private:
	int _a = 0;
	static int _b = 0;

答案是否定的,因为这里的值是给初始化列表看的,但是我们的static成员都不在类里面,所以这种方法是行不通的

而静态成员的其中一个特性就是——静态成员变量必须在类外面初始化

而类中的只是声明,而类外面的是定义,定义时不加关键字static,但是需要指定类域,如下:

class A
{
private:
	int _a;
	static int _b;
};

int A::_b = 0;

static函数

如果我们将static成员定义为私有,那我们就无法在类外面使用该成员

由此,C++里就搞出了一个static函数,如下:

class A
{
public:

	static int get_b()
	{
		return _b;
	}

private:
	int _a;
	static int _b;
};

int A::_b = 0;

如上,我们写了一个函数get_b,通过将这个函数设为公有我们就能取到_b

并且由于这个函数是static函数,所以没有this指针,也就无法访问到除了静态成员以外的其他成员

友元friend

友元特性简介

当一个类A有特殊情况,必须要访问类B中的私有成员时,我们就可以使用友元

如下:

class A
{
	friend class B;
	int _a = 1;
};

class B
{
public:
	int getnum()
	{
		return _t._a;
	}
private:
	A _t;
};

int main()
{
	B b1;
	cout << b1.getnum() << endl;
	return 0;
}

我们能看到,类A中的_a是私有的,这就意味着其他类是无法使用的,但是我们可以将类B设置为类A的友元,这样子我们的类B就能访问到类A的私有成员了

添加友元的方式就是在类的声明前面加一个friend

当然还有函数的友元,如下:

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}

如上是我们在上文中讲到的Date类(日期类),如若有不了解的,可以点下面链接复习一下:

Date 日期类的实现

和友元类差不多,都是声明前面加一个friend

友元关系讲解

这里关系到一个问题,我们可以联系现实:

我喜欢你,你就一定得喜欢我吗?

我把你当朋友,你就一定得把我当朋友吗?

我是你爸的朋友,我就一定得是你的朋友吗?

这几句话就讲解了:友元关系不是相互的,我把你当朋友,你可以用我的东西,但是我不一定能用你的东西,除非你把我当成朋友

同时,友元关系是不能继承的,这个知识点了解就好,这关系到以后会讲的继承章节的相关内容

内部类

内部类相对来说比较简单

我们在一个类里面,能定义一个int变量,能定义一个static变量,同样的,我们也可以定义一个类类型的变量,也就是在类里面定义一个类

特性一

class A
{
	int _a = 0;
	static int _s;

	class B // 内部类
	{
		int _k;

		void getAnum(const A& a)
		{
			cout << _s << endl;
			cout << a._a << endl;
		}
	};

};
int A::_s = 0;

如上,我们可以总结出内部类的其中一个特性:

内部类天生就是外部类的友元,即内部是外部的朋友,所以内部类可以使用外部类的私有

但是外部类不是内部类的友元,所以外部类不能使用内部类的私有

也就是:不是我把你当朋友,你就一定得把我当朋友

特性二

我们再来看这么一段代码:

class A
{
	int _a = 0;
	static int _s;

	class B // 内部类
	{
		int _k;

		void getAnum(const A& a)
		{
			cout << _s << endl;
			cout << a._a << endl;
		}
	};

};
int A::_s = 0;

int main()
{
	A a1;
	cout << sizeof(a1) << endl;
	return 0;
}

我们可以看到,对类A的大小进行统计以后发现,类B是不计在类A里面的

由此我们可以得出第二个特性:

内部类和外部类是两个平行的类,内部类唯一的特殊点就是受到类域的限制和天生友元关系

也就是说,这两个类之间并没有说谁大谁小,都是两个一样的类,只不过需要访问内部类的话就需要对类域进行限制,如下:

A a1;
A::B _b;

匿名对象

我们来看这么一个例子(假设有一个类A已存在):

A a1; //有名对象
A(); //匿名对象

匿名对象和有名对象的区别在于:有名对象的生命周期是当前域结束时才到

但是匿名对象的生命周期是他所在的那一行,也就是即用即销毁

我们来看下面这一段代码:

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
};

int main()
{
	A a1;
	A();
	A a2;
	return 0;
}

我们可以明显看到,匿名对象那里是刚声明完就销毁了,而有名对象是直到程序结束了才销毁

至于匿名对象的好处,就是调用起来会更省事

现在看起来可能会觉得没什么用,但是当我们在学习STL相关知识的时候就能体会到真香定律(bushi

举个栗子:

#include<map>

int main()
{
	map<int, int> m1;

	//使用有名对象的插入方式
	pair< int, int> s1(1, 2);
	m1.insert(s1);

    //使用匿名对象的插入方式
    m1.insert(pair<int, int>(1, 2));

	//使用隐式类型转换为匿名对象的插入方式
	m1.insert({ 1,2 });

	return 0;
}

map是stl中的一个数据结构,而我们每次插入数据都需要插入一个pair类型的数据进去

如果是有名对象的话,我们就需要将pair类型的变量定义出来再插入进去(s1)

但如果是匿名对象的话,我们直接在插入里面定义一个匿名对象,即用即销毁也无所谓,反正数据插入进去了

当然还有更好的方法,就是走隐式类型转换,当然这些显然有点超纲,我们只需要知道匿名在未来的学习中非常常见即可

结语

距离上一篇更新已经过去了三个月整了,这三个月一直忙于C++和算法,现在也算是抽出时间来补一补博客了,突然有种高中知识学完了但发现初中的作业还没写完的感觉(苦笑)

如果感觉这篇博客对各位有帮助的话,希望可以多多支持喔!!!


网站公告

今日签到

点亮在社区的每一天
去签到