🔥个人主页 :guoguoqiang. 🔥专栏:我与C++的爱恋
朋友们大家好!本篇是类和对象的最后一个部分。
一、static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
统计A类中创建了多少个对象
cpp
class A
{
public:
A()
{}
A(const A& a)
{
}
private:
};
A Func()
{
A aa;
return aa;
}
int main()
{
A aa1;
A aa2;
Func();
return 0;
}
我们可以想象一下用了多少次构造函数就创建了多少对象,我们可以通过全局变量来计数
cpp
int count=0;
class A
{
public:
A()
{
++count;
}
A(const A& a)
{
++count
}
private:
};
A Func()
{
A aa;
return aa;
}
int main()
{
A aa1;
A aa2;
Func();
cout<<count<<endl'
return 0;
}
但这个count是全局变量可能会被随意修改,能不能把他封装到类中呢?
cpp
class A
{
public:
A()
{
++count;
}
A(const A& a)
{
++count
}
private:
int count=0;
};
但是这里会是临时拷贝可能不是同一个count;
这里需要设置为静态变量
private:
static int count=0;
静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。所以需要在类外定义
cpp
class A
{
public:
A()
{
++count;
}
A(const A& a)
{
++count
}
private:
static int count=0;
};
int A::count=0;
这个count则受到类的限制,无法随意访问,如果想访问count,有两种办法:
方法一,将count改为公有,但是破坏了封装性,不建议
方法二,get函数
cpp
class A
{
public:
A() {
++_scount;
}
A(const A & t) {
++_scount;
}
~A() {
--_scount;
}
static int GetACount() { //get
return _scount;
}
private:
static int _scount;
};
//int A::_scount = 0; 方式一不推荐
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
cpp
class A
{
public:
A()
{
++count;
}
A(const A& a)
{
++count
}
static int Getcount(){
return count;
}
private:
static int count=0;
};
静态成员函数在类中有特殊的作用和行为.
静态成员函数不能调用非静态成员函数,
非静态成员函数可以调用静态成员函数。
静态成员函数通常用于提供一些与类的任何特定实例无关的功能,或者访问静态成员变量,而不依赖于类的对象。在设计类时,如果某个函数的行为不需要依赖于对象的状态,那么就应该将其声明为静态的
1特性
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2.静态成员变量必须在类外定义 ,定义时不添加static关键字,类中只是声明
3.类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员也是类的成员,受public、protected、private 访问限定符的限制
static成员 不能给缺省值,因为缺省值是给初始化列表,静态区不存在对象中不走初始化列表。
二、友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以
友元不宜多用。
友元分为:友元函数和友元类
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。
1.重载<<和>>
cpp
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
cpp
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
我们之前重载时会不符合常规调用。
作为成员函数重载,this指针占据了第一个参数,意味着Date必须是左操作数
所以这个函数只能写成全局函数,这里访问不了私有先置为公有
cpp
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
//private:
int _year;
int _month;
int _day;
};
void operator<<(ostream& out,const Date &d)
{
out << d._year << "-" << d._month << "-" <<d._day;
}
int main()
{
Date d1(2024, 4, 22);
cout << d1;
return 0;
}
这下可以访问了,但不能连续赋值。
cpp
Date d1(2024, 4, 22);
Date d2(2024, 4, 20);
cout << d1<<d2;
在这里我们可以理解为cout是从左往右进行的 cout<<d1 返回cout,返回后再继续流输出。
cpp
ostream& operator<<(ostream& out,const Date &d)
{
out << d._year << "-" << d._month << "-" <<d._day;
}
同理可以写出流提取:
cpp
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
2.友元函数
现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。
cpp
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;
};
友元函数的特点
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
3.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
cpp
class Time
{
friend class Date;//日期类中可以直接访问Time中的私有成员变量,但是Time中不能访问Date的私有
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
需要注意:
1.友元关系是单向的,不具有交换性。比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递。如果C是B的友元, B是A的友元,则不能说明C时A的友元。
3.友元关系不能继承。
三、内部类(内部类是外部类的私有)
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类(仅仅受到类域限制),它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限
内部类就是外部类的友元类(内部类可以访问外部类),参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元
cpp
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void fun(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.fun(A());
return 0;
}
B可以访问A的所有成员
特性:
1.内部类可以定义在外部类的public、protected、private都是可以的。
2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3.sizeof(外部类)=外部类,和内部类没有任何关系
cpp
A::B b;
B这个类受到A类的类域的限制
四、匿名对象
cpp
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
有名对象:
A aa1 A aa2(2)
匿名对象:
A() A(11)
cpp
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A aa1(1);
A();
// 我们可以这么定义匿名对象,匿名对象的特点不用取名字,
// 但是他的生命周期只有这一行,下一行他就会自动调用析构函数
return 0;
}
其中第二,三行是A()的构造函数和析构函数。
cpp
class Solution {
public:
int Sum_Solution(int n) {
//...
return n;
}
};
int main()
{
Solution().Sum_Solution(10);
return 0;
}
匿名对象在这样场景下就很好用,当我需要一个临时对象去调用其成员函数,但又不想为这个临时使用的对象创建一个具体的变量名,这样使用就很方便。
拷贝对象时的一些编译器优化:
隐式类型,连续构造+拷贝构造->优化为直接构造
一个表达式中,连续构造+拷贝构造->优化为一个构造
一个表达式中,连续拷贝构造+拷贝构造->优化为一个拷贝构造
本节内容到此结束!感谢大家观看!!