✨✨ 欢迎大家来到贝蒂大讲堂✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:C++学习
贝蒂的主页:Betty’s blog
1. 隐式类型转换
在学习C语言时我们就明白,当我们进行赋值时,如果赋值两边的类型不同时就可能发生隐式类型转换。
1.1. 内置类型
在发生隐式类型转换时,如果都是内置类型就会先开辟一个临时变量,再将右操作数强制类型转换为左操作数的类型,最后用这个临时变量对左操作数进行赋值。注意:这个临时变量具有常性,不可修改。
int main()
{
double j = 1.1;
int i = j;//隐式类型转换
int& a = j;//error
const int& b = j;//ok
return 0;
}
因为临时变量具有常性,所以无法被修改。如果赋值给普通引用就会造成权限的放大,所以只能用常引用。
1.2. 自定义类型
如果将一个内置赋值给自定义类型,那么编译器也会先创造一个自定义类型的**临时变量,**然后用这个内置类型调用构造函数对临时变量初始化,最后用这个临时变量对左操作数进行拷贝构造。。注意:这个临时变量也具有常性,不可修改。
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1 = 2023;//发生隐式类型转换
Date d2 = { 2023,2 };
d1.Print();
d2.Print();
return 0;
}
1.3. explicit关键字
用explicit修饰构造函数,将会禁止构造函数的隐式转换。
class Date
{
public:
explicit Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1 = 2023;//发生隐式类型转换
Date d2 = { 2023,2 };
d1.Print();
d2.Print();
return 0;
}
2. 类的静态成员
2.1. 定义
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。
class A
{
public:
static int Print()//静态成员函数
{
cout << "Print()" << endl;
}
private:
static int _a;//静态成员变量
};
2.2. 注意
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制。
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。所以可以通过类名::静态成员或者 对象.静态成员来访问。
class A
{
public:
static void Print()//静态成员函数
{
cout << "Print()" << endl;
}
private:
static int _a;//静态成员变量
};
int main()
{
A a;
a.Print();
A::Print();
return 0;
}
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
int A::_a = 1;//类外定义
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。比如说:静态函数就无法访问非静态函数。
static void Print()//静态成员函数
{
cout << "Print()" << endl;
Add(1, 2);//无法访问非静态成员
}
int Add(int x, int y)
{
return x + y;
}
但是非静态成员函数能访问静态成员函数。
static void Print()//静态成员函数
{
cout << "Print()" << endl;
}
int Add(int x, int y)
{
return x + y;
Print();
}
3. 友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类。
3.1. 友元函数
有时在类外我们需要访问类中的数据,但由于访问限定符的限制并不能访问私有成员。这时如果一定要访问的话就需要借助我们的友元函数,它的用法十分简单,只用在类中加入friend+该函数的声明
。
class Date
{
friend void Print(const Date& d);//友元函数
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
void Print(const Date&d)
{
cout << d._year << "-" << d._month << "-" << d._day << endl;
}
当然我们也能通过在类中声明获取私有元素的返回函数实现:
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
int GetYear()const
{
return _year;
}
int GetMonth()const
{
return _month;
}
int GetDay()const
{
return _day;
}
private:
int _year;
int _month;
int _day;
};
void Print(const Date& d)
{
cout << d.GetYear() << "-" << d.GetMonth() << "-" << d.GetDay() << endl;
}
注意:
- 友元函数是可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用原理相同
3.2. 友元类
除了友元函数外,还有一种友元类。友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。与友元函数用法类似:在一个类中声明friend+class+类名
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
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;
};
注意:
- **友元关系是单向的,不具有交换性。**比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
- **友元关系不能传递。**如果C是B的友元, B是A的友元,则不能说明C时A的友元。
- 友元关系不能继承。在继承位置再给大家详细介绍。
4. 内部类
4.1. 定义
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。并且内部类就是外部类的友元类。
class A
{
public:
class B//B是A的友元
{
private:
int _m;
};
private:
int _a;
int _b;
};
4.2. 注意
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象 / 类名。
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo()
{
cout << k << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo();
return 0;
}
- 外部类的大小与内部类的大小没有关系,sizeof(外部类) = 外部类。
class A
{
private:
int k;
int h;
public:
class B // B天生就是A的友元
{
public:
int _a;
};
};
int main()
{
A a;
cout << sizeof(a) << endl;
return 0;
}
5. 匿名对象
匿名对象与C语言中的匿名结构体类似,只有类名,作用域只在匿名对象声明的一行。
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
cout << "Date" << endl;
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
cout << "Date(const Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
~Date()
{
cout << "~Date()" << endl;
_year = _month = _day = 0;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date();//匿名对象
Date d;
return 0;
}
但是我们也能通过引用延长匿名对象的生命周期。
int main()
{
const Date& dc = Date();//匿名对象也具有常性
Date d;
return 0;
}
6. 编译器的一些优化
下面我们将介绍一些编译器对自定义类型常见的优化,我们还是以日期类举例:
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
cout << "Date" << endl;
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
cout << "Date(const Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
~Date()
{
cout << "~Date()" << endl;
_year = _month = _day = 0;
}
private:
int _year;
int _month;
int _day;
};
- 直接构造+拷贝构造=>直接构造
- 直接构造+拷贝构造+拷贝构造=>直接构造
- 直接构造+直接构造+拷贝构造+赋值重载=>直接构造+直接构造
7. 日期类的模拟实现
下面让我们来实现一个功能比较丰富的日期类。
7.1. 项目功能
- 日期之间的比较(>,<,>=,<=,==,!=)。
- 日期的递增与递减。
- 日期+天数(+,+=)。
- 日期-天数(-,-=)。
- 日期-日期。
- 流输入与流输出。
- 打印日期。
7.2. 功能实现
7.2.1. 构造与析构函数
在写构造函数之前我们需要写一个函数判断当前日期是否合法。
int Date:: GetMonDay(int year, int month)//或许当月天数
{
int months[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = months[month];
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
day++;
}
return day;
}
构造函数与拷贝构造与赋值重载。注意:缺省参数在定义时不需要书写
Date::Date(int year, int month, int day)//构造
{
assert(month > 0 && month < 13);
int days = GetMonDay(year, month);
if (day >= 0 && day <= days)
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "输入日期不合法" << endl;
}
}
Date::Date(const Date& d)//拷贝构造
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& Date::operator=(const Date& d)//赋值重载
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
析构函数:
Date::~Date()
{
_year = _month = _day = 0;
}
7.2.2. 两个日期之间的比较
bool Date::operator==(const Date& d) const//判断是否相当
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d) const//判断是否不相等
{
return !(*this == d);
}
bool Date::operator>(const Date& d) const//大于
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
else
{
return false;
}
}
bool Date::operator>=(const Date& d) const//大于等于
{
return *this == d || *this > d;
}
bool Date::operator<(const Date& d)const//小于
{
return !(*this >= d);
}
bool Date::operator<=(const Date& d)const//小于等于
{
return !(*this > d);
}
7.2.3. 自增与自减
为了区分前置与后置的区别,C++规定在重载时前置并不需要额外的参数,后置需要一个额外的int参数。
Date& Date::operator++()//前置++
{
*this += 1;
return *this;
}
Date Date::operator++(int)//后置++
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()//前置--
{
*this -= 1;
return *this;
}
Date Date::operator--(int)//后置--
{
Date tmp(*this);
*this -= 1;
return tmp;
}
7.2.4. 日期与天数直接的计算
Date& Date::operator+=(int day)//+=
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonDay(_year, _month))
{
_day -= GetMonDay(_year, _month);
++_month;
if (_month > 12)
{
_month = 1;
_year++;
}
}
return *this;
}
Date Date::operator+(int day)const//+
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator-=(int day)//-=
{
if (day < 0)
{
*this += -day;
return *this;
}
while (day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)const//-
{
Date tmp(*this);
tmp -= day;
return tmp;
}
int Date::operator-(const Date& d)const//相差
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}//求最大
int n = 0;
while (min != max)
{
++min;
++n;
}
return flag * n;
}
7.2.5. 打印
void Date::Print()const//打印
{
cout<< _year << "/" << _month << "/" << _day;
}
7.2.6. 流插入与流提取
因为重载后的运算符顺序与参数顺序相同,为了符合习惯,我们只好在类外定义。并且因为函数较短,可以声明为了内联函数。
inline ostream& operator<<(ostream& out, const Date& d)//流输出
{
out << d._year << "/" << d._month << "/" << d._day <<endl;
return out;
}
inline istream& operator>>(istream& in, Date& d)//流输入
{
in >> d._year >> d._month >> d._day;
return in;
}
7.3. 完整代码
7.3.1. Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
friend inline ostream& operator<<(ostream& out, const Date& d);
friend inline istream& operator>>(istream& in, Date& d);
public:
int GetMonDay(int year, int month);//或许当月天数
Date(int year = 1, int month = 1, int day = 1);//构造
Date(const Date& d);//拷贝构造
Date& operator=(const Date& d);//赋值重载
bool operator==(const Date& d) const;//判断是否相当
bool operator!=(const Date& d) const;//判断是否不相等
bool operator>(const Date& d) const;//大于
bool operator>=(const Date& d) const;//大于等于
bool operator<(const Date& d)const;//小于
bool operator<=(const Date& d)const;//小于等于
Date& operator+=(int day);//+=
Date operator+(int day)const;//+
Date& operator-=(int day);//-=
Date operator-(int day)const;//-
Date& operator++();//前置++
Date operator++(int);//后置++
Date& operator--();//前置--
Date operator--(int);//后置--
int operator-(const Date& d)const;//相差
void Print()const;//打印
~Date();
private:
int _year;
int _month;
int _day;
};
inline ostream& operator<<(ostream& out, const Date& d)//流输出
{
out << d._year << "/" << d._month << "/" << d._day <<endl;
return out;
}
inline istream& operator>>(istream& in, Date& d)//流输入
{
in >> d._year >> d._month >> d._day;
return in;
}
7.3.2. Date.cpp
#include"Date.h"
int Date:: GetMonDay(int year, int month)//或许当月天数
{
int months[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = months[month];
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
day++;
}
return day;
}
Date::Date(int year, int month, int day)//构造
{
assert(month > 0 && month < 13);
int days = GetMonDay(year, month);
if (day >= 0 && day <= days)
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "输入日期不合法" << endl;
}
}
Date::Date(const Date& d)//拷贝构造
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& Date::operator=(const Date& d)//赋值重载
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
bool Date::operator==(const Date& d) const//判断是否相当
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d) const//判断是否不相等
{
return !(*this == d);
}
bool Date::operator>(const Date& d) const//大于
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
else
{
return false;
}
}
bool Date::operator>=(const Date& d) const//大于等于
{
return *this == d || *this > d;
}
bool Date::operator<(const Date& d)const//小于
{
return !(*this >= d);
}
bool Date::operator<=(const Date& d)const//小于等于
{
return !(*this > d);
}
Date& Date::operator+=(int day)//+=
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonDay(_year, _month))
{
_day -= GetMonDay(_year, _month);
++_month;
if (_month > 12)
{
_month = 1;
_year++;
}
}
return *this;
}
Date Date::operator+(int day)const//+
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator-=(int day)//-=
{
if (day < 0)
{
*this += -day;
return *this;
}
while (day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)const//-
{
Date tmp(*this);
tmp -= day;
return tmp;
}
Date& Date::operator++()//前置++
{
*this += 1;
return *this;
}
Date Date::operator++(int)//后置++
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()//前置--
{
*this -= 1;
return *this;
}
Date Date::operator--(int)//后置--
{
Date tmp(*this);
*this -= 1;
return tmp;
}
int Date::operator-(const Date& d)const//相差
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}//求最大
int n = 0;
while (min != max)
{
++min;
++n;
}
return flag * n;
}
void Date::Print()const//打印
{
cout<< _year << "/" << _month << "/" << _day;
}
Date::~Date()
{
_year = _month = _day = 0;
}