当前位置:   article > 正文

C++从零开始的打怪升级之路(day7)

C++从零开始的打怪升级之路(day7)

这是关于一个普通双非本科大一学生的C++的学习记录贴

在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料

那么开启正题

今天把类和对象的剩下资源都结束了,学了一些零碎的小知识点,整体难度一般,明天就开始内存管理了(激动!)

1.再谈构造函数

1.1构造函数体赋值

通过前面的学习,我们知道类的实例化创建对象时,会自动调用构造函数

  1. class Date
  2. {
  3. public:
  4. Date(int year = 0, int month = 1, int day = 1)
  5. {
  6. _year = year;
  7. _month = month;
  8. _day = day;
  9. }
  10. private:
  11. int _year;
  12. int _month;
  13. int _day;
  14. };

上面的日期类里就有一个我们定义的构造函数,要注意的时构造函数完成的是初赋值,而不是初始化,对象的初始化只能初始化一次,而构造函数内可以多次赋值

1.2初始化列表

初始化列表::以一个冒号开始,接着是以逗号分隔的数据成员列表,每个"成员变量"后面跟着一个放在括号中的初始值或表达式

  1. class Date
  2. {
  3. public:
  4. Date(int year = 0, int month = 1, int day = 1)
  5. :_year(year)
  6. ,_month(month)
  7. ,_day(day)
  8. {}
  9. private:
  10. int _year;
  11. int _month;
  12. int _day;
  13. };

我们可以用前面学过的方法给对象成员初始化,为什么还要弄一个初始化列表出来呢

我们来看下面的代码

  1. class C
  2. {
  3. public:
  4. C(int c)
  5. {}
  6. private:
  7. int c;
  8. };
  9. int main()
  10. {
  11. int& a;
  12. const int b;
  13. C c;
  14. return 0;
  15. }

编译器在定义上面的a(引用),b(const修饰的变量),c(没有默认构造函数的类)的时候会报错, 因为它们初始化的时候就必须赋值,这个时候就体现出来了成员列表优先初始化,函数体内实现赋值的作用,它们之间是可以相互协助的,不是有你无我,有我无你的关系

注意::

1.每个成员函数在初始化列表中只能存在一次,也就是说初始化只能有一次

2.类中包含下面的这样的成员时,必须在初始化列表进行初始化

a.引用成员变量

b.const成员

c.自定义类型成员(该类没有默认构造函数)

这里我在带大家回顾以下几种默认构造函数,所谓的默认构造函数就是不用传参就可以调用的函数

编译器自动生成的构造函数

全缺省构造函数 

无参的构造函数

3.尽量使用初始化列表初始化,因为不管我们是否使用初始化列表,对于自定义成员变量,一定会使用初始化列表进行初始化

  1. class Time
  2. {
  3. public:
  4. Time(int hour = 0)
  5. :_hour(hour)
  6. {
  7. cout << "Time()" << endl;
  8. }
  9. private:
  10. int _hour;
  11. };
  12. class Date
  13. {
  14. public:
  15. Date(int day)
  16. {}
  17. private:
  18. int _day;
  19. Time _t;
  20. };
  21. int main()
  22. {
  23. Date d(1);
  24. return 0;
  25. }

利用上面的代码可以很好得证实这一点

4.成员变量在类的声明次序就是在初始化列表的初始化顺序,与其在初始化列表中的先后顺序无关

  1. class A
  2. {
  3. public:
  4. A(int a = 0)
  5. :_a2(a)
  6. ,_a1(_a2)
  7. {}
  8. private:
  9. int _a1;
  10. int _a2;
  11. };
  12. int main()
  13. {
  14. A a;
  15. return 0;
  16. }

利用上面的代码可以很好的证实这一点,如果初始化顺序是按声明次序来的,那么a1,a2都是随机值,如果按初始化列表顺序就都是0,大家可以自己运行试一下

1.3explicit关键字

构造函数不仅可以构造和初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还有类型转化的作用,而explicit关键词加在构造函数的前面就可以禁止类型转化

  1. class Date
  2. {
  3. public:
  4. Date(int day)
  5. {
  6. cout << "Date()" << endl;
  7. }
  8. Date(const Date& d)
  9. {
  10. _day = d._day;
  11. cout << "Date(const Date& d)" << endl;
  12. }
  13. private:
  14. int _day;
  15. };
  16. int main()
  17. {
  18. Date d1(0);
  19. Date d2(d1);
  20. Date d3 = d1;
  21. d1 = 1;
  22. return 0;
  23. }

依照上面的代码和运行结果,我们知道对于只有一个类型的参数(或第一个无默认参数其他都有的)构造函数

实际上,在上述代码下,会用那个1创建一个临时对象,再将临时变量传给d1,但是经过编译器处理中间步骤变简略了

在C++11中支持多参数转化,用{}引起来,逗号分隔参数

这样的代码可读性很差,所以有了explicit来禁止这样的转化发生

2.static成员

2.1.概念

声明为static的类成员称为静态成员,用static修饰的成员变量,称之为静态成员变量,用static修饰的成员函数,称之为静态成员函数,静态成员变量一定要在类外进行初始化

这里我们实现一个类,计算程序当中创建了多少个对象

  1. class A
  2. {
  3. public:
  4. A()
  5. {++_count;}
  6. A(const A& a)
  7. {++_count;}
  8. static int GetCount()
  9. {return _count;}
  10. private:
  11. static int _count;
  12. };
  13. int A::_count = 0;
  14. int main()
  15. {
  16. A a;
  17. cout << A::GetCount() << endl;
  18. return 0;
  19. }

2.2.特性

1.静态成员为所有类的对象所共享,不属于某个具体的对象,存放在静态区

2.静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

3.类静态成员即可用类名::静态成员或者对象::静态成员来访问

4.静态成员函数没有隐含的this指针,不能访问如何非静态成员

5.静态成员也是类的成员,受public,protected,private访问限定符的限制

a.静态成员函数不可以调用非静态成员函数

b.非静态成员函数可以调用静态成员函数

3.友元

3.1友元函数

如果我们要重载operator<<,发现没办法在类里面实现,因为this指针的存在,cout无法出现在第一个操作数的位置,但是我们在类外面定义这个函数的时候,又无法访问成员,此时就需要友元来解决,operator>>同理

  1. class Date
  2. {
  3. friend ostream& operator<<(ostream& out, const Date d);
  4. public:
  5. Date(int year = 0, int month = 1, int day = 1)
  6. :_year(year)
  7. ,_month(month)
  8. ,_day(day)
  9. {}
  10. private:
  11. int _year;
  12. int _month;
  13. int _day;
  14. };
  15. ostream& operator<<(ostream& out, const Date d)
  16. {
  17. out << d._year << "-" << d._month << "-" << d._day;
  18. return out;
  19. }

注意:
1.友元函数可以访问类的私有和保护,但是不是类的成员函数

2.友元函数不能被const修饰

3.友元函数可以在类定义的任何地方声明,不受访问限定符的限制

4.一个函数可以是多个类的友元函数

5.友元函数的调用与普通函数的调用原理相同

3.2友元类

如果在一个类中需要多次用到友元函数,我们可以考虑使用友元类简化代码

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

注意:
1.友元关系是单向的

2.友元关系不能传递

3.友元关系不能继承,在继承位置再具体学习

  1. class Time
  2. {
  3. friend class Date;
  4. public:
  5. Time(int hour = 0)
  6. :_hour(hour)
  7. {};
  8. private:
  9. int _hour;
  10. };
  11. class Date
  12. {
  13. public:
  14. Date(int day = 1)
  15. :_day(day)
  16. {}
  17. void Print()
  18. {
  19. cout << _day << "-" << _t._hour << endl;
  20. }
  21. private:
  22. int _day;
  23. Time _t;
  24. };

4.内部类

4.1概念

如果一个类定义在另一个类的内部,这个类就叫做内部类,内部类是一个独立的类,不属于外部类,更不能通过外部类的对象来访问内部类的对象,外部类对内部类没有任何的优越的访问权限

内部类就是外部类的友元类

4.2特性

1.内部类可以定义在外部类的public,private,protected都是可以的

2.内部类可以直接访问外部类的static成员,不需要外部的类名/对象

3.sizeof(外部类)=外部类,和内部类没有关系

实际当中内部类用的比较少,这里不深究

5.匿名对象

匿名对象的生命周期只在它创建的那一行,当只有一行需要用到这个对象时候很好用,其他行找不到这个匿名对象

匿名对象的创建就是类名后面加(),这行执行完后,匿名对象立马调用析构函数进行清理

  1. class num
  2. {
  3. public:
  4. num(int num = 0)
  5. :_num(num)
  6. {
  7. cout << "num()" << endl;
  8. }
  9. int GetNum()
  10. {
  11. return _num;
  12. }
  13. ~num()
  14. {
  15. cout << "~num()" << endl;
  16. }
  17. private:
  18. int _num;
  19. };
  20. int main()
  21. {
  22. num a;
  23. cout << a.GetNum() << endl;
  24. cout << num().GetNum() << endl;
  25. return 0;
  26. }

6.再次理解类和对象

类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象

总结::类和对象告一段落,明天开始内存管理的学习!!!

今天的博客就到这里了,后续内容明天分享,最近因为考试周原因不能更新太多内容,等考试周结束了再"快马加鞭"

新手第一次写博客,有不对的位置希望大佬们能够指出,也谢谢大家能看到这里,让我们一起学习进步吧!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/65905
推荐阅读
相关标签
  

闽ICP备14008679号