类和对象(下+)_const成员、初始化列表、友元、匿名对象 ...

打印 上一主题 下一主题

主题 554|帖子 554|积分 1662

类和对象(下+)



  

媒介

static成员、内部类、const成员、初始化列表、友元、匿名对象

一、const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,现实修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
其特性如下:
  1. #define  _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. using namespace std;
  4. class Date
  5. {
  6. public:
  7.         Date(int year, int month, int day)
  8.         {
  9.                 _year = year;
  10.                 _month = month;
  11.                 _day = day;
  12.         }
  13.         void Print()//Date* this
  14.         {
  15.                 cout << _year << "年" << _month << "月" << _day << "日" << endl;
  16.         }
  17.         void Print()const //const Date* this
  18.         {
  19.                 cout << _year << "年" << _month << "月" << _day << "日" << endl;
  20.         }
  21. private:
  22.         int _year; // 年
  23.         int _month; // 月
  24.         int _day; // 日
  25. };
  26. int main()
  27. {
  28.         Date d1(2028, 1, 11);
  29.         d1.Print();
  30.         const Date d2(2028, 8, 18);
  31.         d2.Print();
  32.         return 0;
  33. }
复制代码
匹配原则(找最符合的)【权限不能放大】:
d1调用第一个Print(带const修饰)
d2调用第二个Print(不带const修饰

认为是2钟差异的类型,构成函数重载
但是在这里如许使用const是没故意义的。

写一个简朴的次序表
  1. #define  _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. #include <cassert>
  4. using namespace std;
  5. class SeqList
  6. {
  7. public:
  8.         void PushBack(int x)
  9.         {
  10.                 _a[_size++] = x;
  11.         }
  12.        
  13.         size_t size()const
  14.         {
  15.                 return _size;
  16.         }
  17.        
  18.         int operator[](size_t i)
  19.         {
  20.                 assert(i < _size);
  21.                 return _a[i];
  22.         }
  23. private:
  24.         int* _a = (int*)malloc(sizeof(int) * 10);
  25.         size_t _size = 0;
  26.         size_t _capacity = 0;
  27. };
  28. int main()
  29. {
  30.         SeqList sl;
  31.         sl.PushBack(1);
  32.         sl.PushBack(2);
  33.         sl.PushBack(3);
  34.         for (size_t i = 0; i < sl.size(); i++)
  35.         {
  36.                 cout << sl[i]<<" ";
  37.                 //cout << sl.operator[](i) << " ";//与上式等价
  38.         }
  39.         return 0;
  40. }
复制代码


但是当我们需要修改sl里的内容时是不可以的,原因是重载operator[ ]返回的是int类型的_a的拷贝,具有常性。
这里需要提一下:


此时就可以进行修改了。

   此时如果又需要一个专门用于打印的函数Print,并且在传参时防止sl对象被修改因此加以const修饰,但是此时又会出现新的报错,如下
  1. #define  _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. #include <cassert>
  4. using namespace std;
  5. class SeqList
  6. {
  7. public:
  8.         void PushBack(int x)
  9.         {
  10.                 _a[_size++] = x;
  11.         }
  12.         size_t size()
  13.         {
  14.                 return _size;
  15.         }
  16.        
  17.         int& operator[](size_t i)
  18.         {
  19.                 assert(i < _size);
  20.                 return _a[i];
  21.         }
  22. private:
  23.         int* _a = (int*)malloc(sizeof(int) * 10);
  24.         size_t _size = 0;
  25.         size_t _capacity = 0;
  26. };
  27. void Print(const SeqList& sl)
  28. {
  29.         for (size_t i = 0; i < s.size(); i++)
  30.         {
  31.                 cout << sl[i] << " ";
  32.         }
  33. }
  34. int main()
  35. {
  36.         SeqList sl;
  37.         sl.PushBack(1);
  38.         sl.PushBack(2);
  39.         sl.PushBack(3);
  40.        
  41.         Print(sl);
  42.         return 0;
  43. }
复制代码

   报错原因出在:const对象调用非const对象,存在权限放大的问题
办理方法:
  1.         size_t size() const
  2.         {
  3.                 return _size;
  4.         }
  5.         //只读
  6.         int& operator[](size_t i) const
  7.         {
  8.                 assert(i < _size);
  9.                 return _a[i];
  10.         }
  11.         //读 or 写都可以                  //与上一个代码块构成函数重载
  12.         int& operator[](size_t i)
  13.         {
  14.                 assert(i < _size);
  15.                 return _a[i];
  16.         }
复制代码
只需要在size()和[ ]重载函数中加以const修饰this指针,即可。
   同样看下面这段日期类
  1. class Date
  2. {
  3. public:
  4.         Date(int year, int month, int day)
  5.         {
  6.                 _year = year;
  7.                 _month = month;
  8.                 _day = day;
  9.         }
  10.         void Print()const
  11.         {
  12.                 cout << _year << "年" << _month << "月" << _day << "日" << endl;
  13.         }
  14.         bool operator<(const Date& d)const
  15.         {
  16.                 //this->Print();//this 是非const 的,可以调用const 的Print
  17.                 if (_year < d._year)
  18.                 {
  19.                         return true;
  20.                 }
  21.                 else if (_year == d._year && _month < d._month)
  22.                 {
  23.                         return true;
  24.                 }
  25.                 else if (_year == d._year && _month == d._month && _day < d._day)
  26.                 {
  27.                         return true;
  28.                 }
  29.                 return false;
  30.         }
  31. private:
  32.         int _year; // 年
  33.         int _month; // 月
  34.         int _day; // 日
  35. };
  36. int main()
  37. {
  38.         const Date d1(2028, 1, 11);
  39.         Date d2(2028, 8, 18);
  40.         cout << (d1 < d2) << endl;
  41.         cout << (d1.operator<(d2)) << endl;//与上行代码等价
  42.         return 0;
  43. }
复制代码
  const Date d1(2028, 1, 11);当d1加上const时,如果operator<不加const的话,会出现错误,原因仍旧是d1的类型是const Date*类型,(属于权限放大)
  
二、友元

1.友元函数

友元函数的几点特性:


  • 友元函数可访问类的私有和保护成员,但不是类的成员函数(没有this指针)
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用和普通函数的调用原理类似
   照旧之前说的日期类,此时我们想要重载一下"<<"如下
  1.         void operator<<(ostream& out)
  2.         {
  3.                 out << _year << "年" << _month << "月" << _day << "日" << endl;
  4.         }
复制代码
但是当我们想要使用重载的"<<“时,会出现以下环境:


因此为了能够正常使用,我们在全局定义关于”<<"的重载函数,但在全局定义又会出现无法访问成员变量的问题,因此此时就需要在Date中进行友元声明,如许在全局定义的函数就可以访问类成员变量了
  1. friend void operator<<(ostream& out, const Date& d);
复制代码
接下来为了满足cout<<d1<<d2;需要以上返回out,即:
  1.         ostream& operator<<(ostream& out, const Date& d)
  2.         {
  3.                 out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
  4.                 return out;
  5.         }
复制代码

2.友元类

友元类的全部成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员

  • 友元关系是单向的,不具有交换性
  • 友元关系不能通报
  • 友元关系不能继承
  1. class Time
  2. {
  3.         friend class Date1;
  4. public:
  5.         friend class Date;        // 声明日期类为时间类的友元类,
  6.                                                 //则在日期类中就直接访问Time类中的私有成员变量
  7.         Time(int hour = 0, int minute = 0, int second = 0)
  8.                 : _hour(hour)
  9.                 , _minute(minute)
  10.                 , _second(second)
  11.         {}
  12. private:
  13.         int _hour;
  14.         int _minute;
  15.         int _second;
  16. };
  17. class Date1
  18. {
  19. public:
  20.         Date1(int year, int month, int day)
  21.         {
  22.                 _year = year;
  23.                 _month = month;
  24.                 _day = day;
  25.         }
  26.         void SetTimeOfDate(int hour, int minute, int second)
  27.         {
  28.                 // 直接访问时间类私有的成员变量
  29.                 _t._hour = hour;
  30.                 _t._minute = minute;
  31.                 _t._second = second;
  32.         }
  33.         void Print()const
  34.         {
  35.                 cout << _year << "年" << _month << "月" << _day << "日" << endl;
  36.         }
  37. private:
  38.         int _year; // 年
  39.         int _month; // 月
  40.         int _day; // 日
  41.         Time _t;
  42. };
复制代码
三、初始化列表

   就像如许
  1. public:
  2.         //初始化列表,是每个成员定义的地方
  3.   Date(int year = 1900, int month = 1, int day = 1)
  4.       : _year(year)
  5.       , _month(month)
  6.       , _day(day)
  7.   {}
  8. private:
  9.         //每个成员的声明
  10.         int _year;
  11.         int _month;
  12.         int _day;
复制代码
需要注意的几点是:

  • 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  • 类中包罗以下成员,必须放在初始化列表位置进行初始化:

    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)

  • 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  • 成员变量在类中声明次序就是其在初始化列表中的初始化次序与其在初始化列表中的先后次序无关

    在构造函数的初始化列表阶段,对内置类型用随机值进行初始化,对自定义类型会调用它的默认构造


当_day给了缺省值 int _day=1;

_day给了缺省值,把它初始化成1 了;接下来还要走函数体变为18,由此可知()缺省值就是给初始化列表用的!!
但是初始化列表也有不能办理初始化问题
(比如要求数组_array初始化一下(在函数体中中memset初始化))
四、explicit关键字

  1. class A
  2. {
  3. public:
  4.         A(int i)
  5.                 :_a(i)
  6.         {
  7.                 cout << "A" << endl;
  8.         }
  9. private:
  10.         int _a;
  11. };
  12. int main()
  13. {
  14.         A aa1(1);
  15.         A aa2 = 2;
  16. }
复制代码

因为


  • 单参数构造函数的隐式类型转换
  • 用2调用A构造函数天生一个暂时对象,再用这个对象去拷贝构造aa2
  • 编译器会再优化,优化用2直接构造


    存在类型转换,会天生暂时对象,2具有常量性,ref不能引用暂时对象,也就是存在权限放大问题,加上const就可以了即:
   const A& ref = 3;
  紧接着另有一个问题,那么为什么这里ret可以引用2,因为因为这个单参数的函数支持隐式类型转换,单参数函数这个参数(2)的的整型值能转换成一个A的对象,就可以引用了。
如果说bu想让隐式类型转换发生,可以加关键字explicit,加在构造函数的位置
  1.         explicit  A(int i)
  2.                 :_a(i)
  3.         {
  4.                 cout << "A" << endl;
  5.         }
复制代码
此时


   以上是讨论的单参数的,那么如果是多参数的呢?
  c++11支持多参数的转换
  1. class B
  2. {
  3. public:
  4.          B(int b1,int b2)
  5.                 :_b1(b1)
  6.                 ,_b2(b2)
  7.         {
  8.                 cout << "B" << endl;
  9.         }
  10. private:
  11.         int _b1;
  12.         int _b2;
  13. };
  14. int main()
  15. {
  16.         B bb1(1, 1);
  17.         B bb2 = {2,2};
  18.         const B& ref = { 3,3 };//同样的,不加const就不支持引用
  19.         return 0;
  20. }
复制代码
道理同上,如果不想让隐式类型转换发生,使用关键字explicit
五、匿名对象

起首来比力一下著名对象和匿名对象:


  • 著名对象 特点:生命周期在当前局部域
   A aa4(4);
  

  • 匿名对象 特点:生命周期只在这一行
   A (5);
  匿名对象可以用来传参,不用先创建变量,再传参
  1. class A{public:        explicit  A(int i)
  2.                 :_a(i)
  3.         {
  4.                 cout << "A" << endl;
  5.         }
  6. 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;}
复制代码
当在一些特定场景下,适当使用匿名对象可以起到简化代码的作用。
总结

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莫张周刘王

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表