运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
赋值运算符重载
要注意,赋值运算符只能定义在类中.赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数
class Date
{
public:
Date(int hour = 00, int min = 00, int se = 0)
{
_hour = hour;
_min = min;
_second = se;
}
void operator=(const Date& d)
{
cout << "void operator=(const Date& d)" << endl;
_hour = d._hour;
_min = d._min;
_second = d._second;
}
private:
int _hour;
int _min;
int _second;
};
int main()
{
Date d1(22, 55, 21);
Date d2 = d1; // ok
Date d3;
d3 = d2 = d1; //err
return 0;
}
// 要去除erro,只要将赋值运算符重载改为
Date& operator=(const Date& d)
{
cout << "Date& operator=(const Date& d)" << endl;
_hour = d._hour;
_min = d._min;
_second = d._second;
return *this;
}
//可以加上if(*this != &d),防止自己给自己赋值(d2 = d2),这样没有意义,写上这句能增快速度
.* :: sizeof ?:(三目) . 注意以上5个运算符不能重载。
const 修饰符
class Date
{
public:
Date(int year = 2023, int month = 9, int day = 26)
{
//cout << "Date(int year, int month, int day)" << endl;
_year = year;
_month = month;
_day = day;
}
void PrintDate() // 参数为Date* const this
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int year;
int _month;
int _day;
};
// const 测试
void Test07()
{
Date d1(2023, 9, 30);
const Date d2(2023, 8, 30);
d1.PrintDate();
d2.PrintDate();
}
上面的代码会报错
因为 d1的类型是 Date* (ok)
d2的类型是 const Date*
把const Date* 传给Date*,出现权限的放大,所以会报错(err)
想要解决,只需要把在函数后面加上const
void PrintDate() const // 参数为const Date* const this
{
cout << _year << " " << _month << " " << _day << endl;
}
权限可以缩小,但是不能放大
初始化列表
- 每个成员变量在初始化列表只能出现一次,(即只初始化一次)
- 以下的成员必须使用初始化列表
没有默认构造函数的自定义类型的成员
引用成员变量
自定义类型成员变量
class A
{
public:
A(int a)
{
_a = a;
}
private:
int _a;
};
class B
{
public:
B(int a, int b, int c) : _a(a), _b(b), _c(c)
{}
private:
A _a; // 没有默认构造函数的自定义类型的成员
int& _b; // 引用成员变量
const int _c; // 自定义类型成员变量
};
-
成员变量在类中声明的顺序就是初始化列表初始化的顺序,与其在列表的先后顺序无关
// 下面程序的运行结果是? class A { public: A(int a) :_a1(a), _a2(_a1) // 在初始化列表过程中可以认为是成员变量的定义 {} void Print() { cout << _a1 << " " << _a2 << endl; } private: // 成员变量的生命 int _a2; int _a1; };
运行结果是 1 随机值
explicit关键字
对于单个参数或者 除第一个参数无默认值其余均有默认值的构造函数
class Date
{
public:
Date(int year) : _year(year)
{
cout << "Date(int year)" << endl;
}
Date(const Date& d)
{
_year = d._year;
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
};
int main()
{
Date d1(2023); // 直接调用构造
Date d2 = 2023; // 构造+拷贝构造: 编译器优化为->直接调用构造
const Date& d2 = 2023; // 先用2023构造了一个临时变量,临时变量具有常性,所以要加const
return 0;
}
/*
运行结果:
Date(int year)
Date(int year)
Date(int year)
*/
如果不想让20行编译器进行优化,可以将在构造函数前加上explicit,禁止这种隐式类型转换
此时,上面的20行,22行均会报错,禁止这种隐式转换
匿名对象
没有名字的对象,声明周期只有这一行
int main()
{
Date(2000);
return 0;
}
// 通过调试可以发现,到第3行结束时,已经调用完了Date类的构造函数和析构函数
静态成员
概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
特点
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- . 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
class A
{
public:
A() {_scount++;}
// 静态成员函数,没有this指针
static int GetCount()
{
//cout << _a << endl; // err
return _scount;
}
private:
int _a;
// 静态成员变量,属于整个类,声明周期在整个程序运行期间,储存在静态区
static int _scount; // 声明
};
int A::_scount = 0; // 在类外定义初始化
int main()
{
A a;
A a1;
A a2;
//cout << A::_scount << endl;
//cout << a._scount << endl;
//cout << a2._scount << endl;
cout << a.GetCount() << endl;
return 0;
}
静态成员函数,不能调用非静态成员函数,因为没有this指针
非静态成员函数可以调用类的静态成员函数
设计一个只能在栈上定义对象的类
class StackOnly
{
public:
static StackOnly CreatObj() // 提供一个静态成员函数供外界使用
{
StackOnly s;
return s;
}
private:
int _a;
StackOnly(int a = 0) : _a(a){} // 将构造函数私有化
};
int main()
{
StackOnly s = StackOnly::CreatObj();
StackOnly s2 = StackOnly::CreatObj();
return 0;
}