当前位置:网站首页>类和对象【下】
类和对象【下】
2022-08-02 07:55:00 【dhdw】
目录
一:初始化成员列表
1. 利用初始化列表初始化
- 在之前我们通过构造函数对类对象进行初始化时使用的是=,像这样
class Date
{
public:
Date(int year=0, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
- 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化
- 构造函数体中的语句只能将其称作为赋初值,而不能称作初始化
- 因为初始化只能初始化一次,而构造函数体内可以多次赋值
- C++为了保证变量只被初始化一次,并且为了提高效率,提供了初始化列表
- 初始化列表以一个冒号开始,之后的每一行一逗号开头分割每个成员变量,每个成员变量后面跟一个放在括号中的初始值或表达式
- 将上面的构造函数使用初识化列表进行初始化
class Date {
public:
Date(int year=0,int month=1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
2. 必须通过初始化列表初识化的对象
- 既然使用=赋值也能给成员变量初值,看起来好像也没有区别,那么除了效率以外,我们为什么还要使用初始化列表呢?
- 答案是有三种变量我们必须在初始化列表初始化
- const成员变量
- 引用成员变量
- 没有默认构造函数的自定义类型
- 这些变量的特点就是在定义时就需要初始化,如果我们不通过初始化列表初识化的话,编译器会报错
- 通过初始化列表初始化:
class Tmp
{
public:
Tmp(int tmp=0)
:_tmp(tmp)
{
}
private:
int _tmp;
};
class Date {
public:
Date(int year=0,int month=1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{
}
void Print() const
{
cout << _year << "-" << _month << "-" << _day;
}
private:
const int _year;
int& _month;
Tmp _day;
};
- 不通过初始化列表初始化(编译器报一堆错):

3. c++11初始化的新玩法
- 我们有时候懒得写构造函数,但是又想给变量初值,那么我们可以在声明变量时给一个初值,像这样
class Date {
public:
void Print() const
{
cout << _year << "-" << _month << "-" << _day;
}
private:
int _year=2022;
int _month=7;
int _day=31;
};

- 但是这并不是在此处就创建这个变量了,我们在声明变量时给的值相当于函数里面的缺省参数,变量被初始化还是在构造函数中
- 如果我们写了构造函数,并给了参数,那么这个缺省值就不起作用了

4. 初始化列表中的初始化顺序
#include<iostream>
using namespace std;
class Date {
public:
Date(int year)
:_month(year)
, _year(_month)
{
}
void Print() const
{
cout << _year << "-" << _month;
}
private:
int _year;
int _month;
};
int main()
{
Date d(2022);
d.Print();
}
- 上面这样的程序会输出什么呢?
- 我们可能会觉得先用2022初始化_month,然后在用_month初始化_year,所以应该输出2022-2022
- 但是变量的初始化顺序和我们声明的顺序是一致的,和初始化列表中的顺序无关,所以先初始化_year,此时_month还未初始化,所以_year是随机值
- 再用2022初始化_month,所以应该输出
随机值-2022
5. explicit关键字
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
{
}
void Print() const
{
cout << _year;
}
private:
int _year;
};
int main()
{
Date d = 2022;
d.Print();
}

- 如图,我们对于单参数的构造函数支持用=赋值的语法
- 它会进行隐式的类型转换,就是用2022构造一个匿名对象,然后再讲这个匿名对象拷贝构造对象d
- 这样编写代码可读性不是很好,我们要是想避免上述情况发生,就可以使用explicit关键字,加在构造函数前面即可

二: 友元
1. 友元函数
- 如果我们想重载运算符<<用于使用cout直接打印Date类,要是我们写成成员函数的话,使用起来会比较奇怪
- 因为this指针默认占据了第一个操作数,所以cout只能作为第二个操作数,使用时就会变成
d<<cout
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
ostream& operator<<(ostream& out)
{
out << _year << "-" << _month << "-" << _day << endl;
return out;
}
private:
int _year=2022;
int _month=7;
int _day=31;
};
int main()
{
Date d;
d << cout;
}

- 为了符合我们平时的习惯,让cout占据第一个操作数,我们就不能将这个函数写成成员函数,要写在类外面作为普通函数
- 可以在类外我们就无法访问Date的private成员,无法实现打印,这时候我们就需要将这个函数写成Date类的友元函数
- 方法是 friend关键字+函数的声明,在类的任意地方加上都行
#include<iostream>
using namespace std;
class Date {
friend ostream& operator<<(ostream& out, const Date& d);
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year=2022;
int _month=7;
int _day=31;
};
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
int main()
{
Date d;
cout<<d;
}

- 说明:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用和原理相同
2. 友元类
- 友元类和友元函数差不多,就是声明A类是B类的友元类,那么A类中的所有成员函数都可以使用B类中的private成员
- 其实就是将A类中所有的成员函数都变成了B类的友元函数
- 下面就通过友元类让Time类的成员打印Date类的成员
#include<iostream>
using namespace std;
class Time;
class Date {
friend Time;
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year=2022;
int _month=7;
int _day=31;
};
class Time
{
public:
Time(int hour = 1)
:_hour(hour)
{
}
void print(const Date& d)
{
cout << d._year << "-" << d._month << "-" << d._day << endl;
}
private:
int _hour;
};
int main()
{
Date d(2022,7,31);
Time t;
t.print(d);
}

- 注意:
- 友元关系是单向的,不具有交换性
- 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
- 友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
3. C++11中的内部类
- 如果我们在一个类A中再定义一个类B,那么B类就是A类的内部类
- 此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类,外部类对内部类没有任何优越的访问权限
- 内部类天生就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中
的所有成员,但是外部类不是内部类的友元
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
class Time
{
public:
Time(int hour = 1)
:_hour(hour)
{
}
void print(const Date& d)
{
cout << d._year << "-" << d._month << "-" << d._day << endl;
}
private:
int _hour;
};
private:
int _year=2022;
int _month=7;
int _day=31;
};
int main()
{
Date d(2022,7,22);
Date::Time t;
t.print(d);
}

三:static成员
1. 初始化
- 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量,用static修饰的成员函数,称之为静态成员函数
- 静态的成员变量一定要在类外进行初始化
- 并且定义时不用加上static,但是要加上域名和::表明初始化的是类中的静态成员变量
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
}
static int GetCount()
{
return count;
}
private:
int _year;
int _month;
int _day;
static int count;
};
int Date::count = 1;
int main()
{
cout << Date::GetCount();
}

2. 特性
- 静态成员为所有类对象所共享,不属于某个具体的实例
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员(非静态成员函数可以访问静态成员函数)
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
边栏推荐
猜你喜欢
随机推荐
I.MX6U-ALPHA开发板(EPIT定时器实验)
ip地址那点事(二)
Button to control the running water light (timer)
解决IDEA安装安装插件慢问题
uniapp 禁止默认返回事件
科技云报道:实现元宇宙,英伟达从打造基础建设平台开始
王学岗-编译出运行的文件
R language plotly visualization: use the plotly visualization model to predict the true positive rate (True positive) TPR and false positive rate (False positive) FPR curve under different thresholds
AcWing 2811. 最长公共子串(后缀自动机 fa 指针的性质)
7.联合索引(最左前缀原则)
那些年我们踩过的 Flink 坑系列
HCIP 第九天
MySQL事务隔离级别详解
Stop mental exhaustion Daily sharing
基于PyTorch的flappy bird游戏
知识点滴 - 为什么一般不用铜锅做菜
MySQL优化之慢日志查询
5分钟搞懂MySQL - 行转列
OneNote 教程,如何在 OneNote 中创建更多空间?
BGP通过MPLS解决路由黑洞








