类和对象(下+)
媒介
static成员、内部类、const成员、初始化列表、友元、匿名对象
一、const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,现实修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
其特性如下:
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <iostream>
- using namespace std;
- class Date
- {
- public:
- Date(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()//Date* this
- {
- cout << _year << "年" << _month << "月" << _day << "日" << endl;
- }
- void Print()const //const Date* this
- {
- cout << _year << "年" << _month << "月" << _day << "日" << endl;
- }
- private:
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
- int main()
- {
- Date d1(2028, 1, 11);
- d1.Print();
- const Date d2(2028, 8, 18);
- d2.Print();
- return 0;
- }
复制代码 匹配原则(找最符合的)【权限不能放大】:
d1调用第一个Print(带const修饰)
d2调用第二个Print(不带const修饰

认为是2钟差异的类型,构成函数重载
但是在这里如许使用const是没故意义的。
写一个简朴的次序表
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <iostream>
- #include <cassert>
- using namespace std;
- class SeqList
- {
- public:
- void PushBack(int x)
- {
- _a[_size++] = x;
- }
-
- size_t size()const
- {
- return _size;
- }
-
- int operator[](size_t i)
- {
- assert(i < _size);
- return _a[i];
- }
- private:
- int* _a = (int*)malloc(sizeof(int) * 10);
- size_t _size = 0;
- size_t _capacity = 0;
- };
- int main()
- {
- SeqList sl;
- sl.PushBack(1);
- sl.PushBack(2);
- sl.PushBack(3);
- for (size_t i = 0; i < sl.size(); i++)
- {
- cout << sl[i]<<" ";
- //cout << sl.operator[](i) << " ";//与上式等价
- }
- return 0;
- }
复制代码

但是当我们需要修改sl里的内容时是不可以的,原因是重载operator[ ]返回的是int类型的_a的拷贝,具有常性。
这里需要提一下:
此时就可以进行修改了。
此时如果又需要一个专门用于打印的函数Print,并且在传参时防止sl对象被修改因此加以const修饰,但是此时又会出现新的报错,如下
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <iostream>
- #include <cassert>
- using namespace std;
- class SeqList
- {
- public:
- void PushBack(int x)
- {
- _a[_size++] = x;
- }
- size_t size()
- {
- return _size;
- }
-
- int& operator[](size_t i)
- {
- assert(i < _size);
- return _a[i];
- }
- private:
- int* _a = (int*)malloc(sizeof(int) * 10);
- size_t _size = 0;
- size_t _capacity = 0;
- };
- void Print(const SeqList& sl)
- {
- for (size_t i = 0; i < s.size(); i++)
- {
- cout << sl[i] << " ";
- }
- }
- int main()
- {
- SeqList sl;
- sl.PushBack(1);
- sl.PushBack(2);
- sl.PushBack(3);
-
- Print(sl);
- return 0;
- }
复制代码
报错原因出在:const对象调用非const对象,存在权限放大的问题
办理方法:
- size_t size() const
- {
- return _size;
- }
- //只读
- int& operator[](size_t i) const
- {
- assert(i < _size);
- return _a[i];
- }
- //读 or 写都可以 //与上一个代码块构成函数重载
- int& operator[](size_t i)
- {
- assert(i < _size);
- return _a[i];
- }
复制代码 只需要在size()和[ ]重载函数中加以const修饰this指针,即可。
同样看下面这段日期类
- class Date
- {
- public:
- Date(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()const
- {
- cout << _year << "年" << _month << "月" << _day << "日" << endl;
- }
- bool operator<(const Date& d)const
- {
- //this->Print();//this 是非const 的,可以调用const 的Print
- 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;
- }
- return false;
- }
- private:
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
- int main()
- {
- const Date d1(2028, 1, 11);
- Date d2(2028, 8, 18);
- cout << (d1 < d2) << endl;
- cout << (d1.operator<(d2)) << endl;//与上行代码等价
- return 0;
- }
复制代码 const Date d1(2028, 1, 11);当d1加上const时,如果operator<不加const的话,会出现错误,原因仍旧是d1的类型是const Date*类型,(属于权限放大)
二、友元
1.友元函数
友元函数的几点特性:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数(没有this指针)
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用和普通函数的调用原理类似
照旧之前说的日期类,此时我们想要重载一下"<<"如下
- void operator<<(ostream& out)
- {
- out << _year << "年" << _month << "月" << _day << "日" << endl;
- }
复制代码 但是当我们想要使用重载的"<<“时,会出现以下环境:
因此为了能够正常使用,我们在全局定义关于”<<"的重载函数,但在全局定义又会出现无法访问成员变量的问题,因此此时就需要在Date中进行友元声明,如许在全局定义的函数就可以访问类成员变量了
- friend void operator<<(ostream& out, const Date& d);
复制代码 接下来为了满足cout<<d1<<d2;需要以上返回out,即:
- ostream& operator<<(ostream& out, const Date& d)
- {
- out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
- return out;
- }
复制代码
2.友元类
友元类的全部成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
- 友元关系是单向的,不具有交换性
- 友元关系不能通报
- 友元关系不能继承
- class Time
- {
- friend class Date1;
- public:
- friend class Date; // 声明日期类为时间类的友元类,
- //则在日期类中就直接访问Time类中的私有成员变量
- Time(int hour = 0, int minute = 0, int second = 0)
- : _hour(hour)
- , _minute(minute)
- , _second(second)
- {}
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date1
- {
- public:
- Date1(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void SetTimeOfDate(int hour, int minute, int second)
- {
- // 直接访问时间类私有的成员变量
- _t._hour = hour;
- _t._minute = minute;
- _t._second = second;
- }
- void Print()const
- {
- cout << _year << "年" << _month << "月" << _day << "日" << endl;
- }
- private:
- int _year; // 年
- int _month; // 月
- int _day; // 日
- Time _t;
- };
复制代码 三、初始化列表
就像如许
- 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成员变量
- 自定义类型成员(且该类没有默认构造函数时)
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
- 成员变量在类中声明次序就是其在初始化列表中的初始化次序,与其在初始化列表中的先后次序无关
在构造函数的初始化列表阶段,对内置类型用随机值进行初始化,对自定义类型会调用它的默认构造
当_day给了缺省值 int _day=1;
_day给了缺省值,把它初始化成1 了;接下来还要走函数体变为18,由此可知()缺省值就是给初始化列表用的!!
但是初始化列表也有不能办理初始化问题
(比如要求数组_array初始化一下(在函数体中中memset初始化))
四、explicit关键字
- class A
- {
- public:
- A(int i)
- :_a(i)
- {
- cout << "A" << endl;
- }
- private:
- int _a;
- };
- int main()
- {
- A aa1(1);
- A aa2 = 2;
- }
复制代码
因为
- 单参数构造函数的隐式类型转换
- 用2调用A构造函数天生一个暂时对象,再用这个对象去拷贝构造aa2
- 编译器会再优化,优化用2直接构造
存在类型转换,会天生暂时对象,2具有常量性,ref不能引用暂时对象,也就是存在权限放大问题,加上const就可以了即:
const A& ref = 3;
紧接着另有一个问题,那么为什么这里ret可以引用2,因为因为这个单参数的函数支持隐式类型转换,单参数函数这个参数(2)的的整型值能转换成一个A的对象,就可以引用了。
如果说bu想让隐式类型转换发生,可以加关键字explicit,加在构造函数的位置
- explicit A(int i)
- :_a(i)
- {
- cout << "A" << endl;
- }
复制代码 此时
以上是讨论的单参数的,那么如果是多参数的呢?
c++11支持多参数的转换
- class B
- {
- public:
- B(int b1,int b2)
- :_b1(b1)
- ,_b2(b2)
- {
- cout << "B" << endl;
- }
- private:
- int _b1;
- int _b2;
- };
- int main()
- {
- B bb1(1, 1);
- B bb2 = {2,2};
- const B& ref = { 3,3 };//同样的,不加const就不支持引用
- return 0;
- }
复制代码 道理同上,如果不想让隐式类型转换发生,使用关键字explicit
五、匿名对象
起首来比力一下著名对象和匿名对象:
A aa4(4);
A (5);
匿名对象可以用来传参,不用先创建变量,再传参
- class A{public: explicit A(int i)
- :_a(i)
- {
- cout << "A" << endl;
- }
- private: int _a;};class SeqList{public: void PushBack(A x) { _a[_size++] = x; } size_t size() const { return _size; } A& operator[](size_t i) const { assert(i < _size); return _a[i]; }private: A* _a = (A*)malloc(sizeof(A) * 10); size_t _size = 0; size_t _capacity = 0;};int main(){ SeqList s; A aa3(3); s.PushBack(aa3); s.PushBack(A(4));//利用匿名对象,直接传参 return 0;}
复制代码 当在一些特定场景下,适当使用匿名对象可以起到简化代码的作用。
总结
|