📋 个人简介
-
💖 作者简介:大家好,我是菀枯😜
-
🎉 支持我:点赞👍+收藏⭐️+留言📝
-
💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️
前言
通过前面一段的时间,我们已经结束了类和对象的大部分内容。剩下的时间呢,我们来解决掉最后一点点的内容,C++类和对象的知识就结束了。
初始化列表
初始化列表:构造函数的一部分,以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个**"成员变量"后面跟一个放在括号中的初始值。**
既然有了构造函数可以对成员变量进行赋值,那么为什么C++中还需要初始化列表呢?
首先我们来看这样一种场景。
class A{
public:
A(int a, int& b, int c){
_a = a;
_b = b;
_c = c;
}
private:
int _a;
int& _b;
const int _c;
};
我们定义了一个A类,A中含有a,b,c三个成员变量。其中b和c都有一个共同的特性,那就是他们只能在被定义的时候初始化,而无法再次被赋值,那么我们原本的构造函数可以为他们两个赋值吗?
我们运行起来看看结果:
int main(){
int a = 1, b = 2, c = 3;
A test(a,b,c);
return 0;
}
我们发现编译失败,编译器不允许我们这样做,那么如何解决这个问题呢?
这个时候,初始化列表就可以发挥它的作用了。
class A {
public:
A(int a, int& b, int c)
:_a(a), //初始化列表
_b(b),
_c(c)
{ }
void Print(){
cout << _a <<" " << _b << " " << _c << endl;
}
private:
int _a;
int& _b;
const int _c;
};
这个时候,我们再来运行一下程序来看看结果。
int main() {
int a = 1, b = 2, c = 3;
A test(a, b, c);
test.Print();
return 0;
}
注意事项
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 无默认构造函数的自定义类型
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
- 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
static成员
在C语言中我们有static修饰的变量,我们都知道有static修饰的变量被创建后,会将变量的值存储到静态区。程序结束时,此变量才会被销毁。
在C++中呢,我们也有static这个关键字,并且产生了静态成员变量和静态成员函数。
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;
用static修饰的成员函数,称之为静态成员函数。
我们来看看这个问题,下面这个类的大小该如何计算
class A{
public:
A(){
++_c;
}
static int Count(){
return _c;
}
private:
int _a;
double _b;
static int _c;
};
int A::_c = 0;
这个类中含有三个成员变量,其中一个为静态变量,如果将静态变量算入对象的大小中,那么这个类的大小就是20个字节,而如果不将此静态变量考虑到这个类中,那么这个类的大小就是16个字节,我们运行起来看看结果:
为我们可以看到,结果为16,这就说明静态类型的成员变量并不是存储在对象的空间中,而是和C语言一样存储到静态区。
特性
-
静态成员为所有类对象所共享,不属于某个具体的实例。
-
静态成员变量必须在类外定义,定义时不添加static关键字。
就像我们刚刚使用的A类中的c成员变量,他的定义在类的外面,类中的_c只是对这个静态成员变量的声明。声明方式如下:
类型 类名::变量名 = 值;
-
类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
int main(){ A a; //(类名::静态成员访问) A::_c; //(对象.静态成员访问) a._c; return 0; }
-
静态成员函数没有隐藏的this指针,不能访问任何非静态成员
-
静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值.
友元
什么是友元
类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
为了解决上述问题呢,C++中引入了友元,友元可以突破封装,让我们直接去访问类中的私有成员。友元分为友元函数和友元类,书写方式如下:
// 友元函数
friend 返回值类型 函数名 (参数列表)
// 友元类
friend class 类名()
和正常的函数和类区别就是在前面加上"friend"关键字即可,表明我是你的朋友,所以我可以访问你的私有成员。-
友元函数
我们先来看下面一段代码:
class Date{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main(){
Date a;
cout << a << endl;
return 0;
}
现在我们是无法使用 cout << 去输出a的,我们需要将 <<进行重载,那么我们该如何实现呢?
这个时候我们就需要用到友元函数来实现这个了。注:下面代码中,ostream为cout 的类型。
class Date{
//在类中声明友元函数
friend ostream& operator<<(ostream& out, const Date& a);
public:
Date(int year = 1, 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& a){
cout << a._year << "-" << a._month << "-" << a._day;
return cout;
}
int main(){
Date a;
cout << a << endl;
return 0;
}
此时友元就可以帮助我们突破封装,让 operator<< 函数直接去访问Date类中的私有成员变量。
友元类
友元类和友元函数形式上差不多,就是在类名前加上friend即可。
class Date{
private:
int _year;
int _month;
int _day;
Time _t
};
class Time{
friend class Date;
private:
int _hour;
int _minute;
int _second
};
此时Date类就是Time类的友元类,Date可突破Time的封装直接去访问Time类中的私有成员。
class Time {
friend class Date;
private:
int _hour;
int _minute;
int _second;
};
class Date {
public:
Date(int year = 1000, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print(int hour, int minute, int second) {
_t._hour = hour;
_t._minute = minute;
_t._second = second;
cout << _year << " " << _month << " " << _day << " "
<< _t._hour << " " << _t._minute << " " << _t._second << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main() {
Date a;
a.Print(10, 12, 50);
return 0;
}
输出结果如下:
在Date类里面可将Time类中的私有成员进行改变。
注意:
友元不具备传递属性,比如A是B的朋友,B是C的朋友,A并不能直接去访问C中的私有成员,而需要通过B来访问C。
友元是单向的,A是B的友元,那么A就可以直接去访问B中的成员,但是B无法去访问A中的成员。
内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的 类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
class A
{
private:
int h;
public:
class B
{
public:
void Print(const A& a)
{
cout << a.h << endl;//OK
}
};
};
此时B就是A的内部类。B天生就是A的友元,它可以突破A的封装直接去访问A中的所有成员。
特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。
结语
欢迎各位参考与指导!!!