一.对象的种类
1.全局对象:
①生命周期:程序结束
②执行顺序:return 0 → } → 析构
2.栈区局部对象:
①生命周期:作用域结束
②执行顺序:return 0 → 析构 → }
3.堆区的指针对象:
①生命周期:遇到delete结束
注:new出来的对象没有delete就没有析构函数 哪怕是作用域结束了也没有
4.临时对象:
①生命周期:仅限当前这一行
②代码说明:
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 CPerson QQ() 8 { 9 CPerson qq; 10 return qq; 11 } 12 }; 13 14 int main() 15 { 16 CPerson kk; 17 kk = kk.QQ(); 18 19 return 0; 20 }
其实到return pp的那一行 return完了就已经把qq删了 所以才会创建一个临时对象接着qq的值
也就是说 下面主函数在第17行执行调用QQ函数的时候 会在这一行创建一个临时对象
临时对象的产生是电脑做的 临时对象的构造函数执行的是电脑里面默认的
用这个临时对象接着函数的返回值 然后把这个临时对象装在kk里面
如果你不想让他创建临时对象的话 可以把函数返回值和参数改成CPerson&类型的
返回值类型如果是CPerson 那么在外面就叫创建临时对象
返回值类型如果是CPerson& 这就相当于只是把一个名字拿出来了
总结:大部分对象都是在堆区new出来的 因为这样比较方便去控制他的生命周期
5.为什么有了malloc和free还要有new和delete呢?(new还是用malloc去定义的)
malloc和free是不调用构造函数和析构函数的 只是单纯的去申请空间和释放空间的
但是!!!
new:①分配空间 ②调用构造和初始化 ③返回空间地址
delete:①调用析构 ②删除对象空间
也就是说:new和delete都可以触发构造函数和析构函数
6.杂乱补充:
①空类的大小:一个字节(后面还会对空类的内容进行补充)
类是抽象的 不存在的 是有定义类的实体 也就是只有定义对象的时候 才会被分配空间
②成员变量和成员函数:
成员变量是在创建对象的时候就存在的 普通的成员变量每个对象都有一份 静态成员变量是所有对象公用一份(这个在后面也会再写 具体有所补充)
成员函数是在编译的时候就存在了 并且只有一份
③不同的地址取得的就是不同的变量 谁调用就用谁的地址 就把谁的地址传进去
普通的成员函数 都会有一个隐藏的参数:CPerson* this(拿CPerson为例)
每一个普通的成员参数都有一个隐藏的指针this 这个指针用来装调用对象的地址 用来区分不同对象的成员
例如:
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 int a; 8 public: 9 CPerson(int b) 10 { 11 a = b; 12 } 13 void Show(/*CPerson* this*/) 14 { 15 cout << this << endl; 16 cout << this -> a << endl; 17 } 18 }; 19 20 int main() 21 { 22 CPerson aa(100); 23 cout << &aa << endl; 24 aa.Show(/* &aa */); 25 26 CPerson bb(200); 27 cout << &bb << endl; 28 bb.Show(/* &bb */); 29 return 0; 30 }
二.类之间的关系
类之间的关系分为两种:
1.纵向关系:继承
①语法:class CSuperman : public CPerson{ ... };
把父类中的所有成员拿过来
横线处的访问修饰符可以替换 访问修饰符不同 继承过来也是有差别的
1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 void ShowEat() 8 { 9 cout << "吃饭" << endl; 10 } 11 }; 12 13 class CSuperMan : public CPerson 14 { 15 public: 16 void ShowFly() 17 { 18 cout << "我能飞" << endl; 19 } 20 }; 21 22 int main() 23 { 24 CSuperMan sm; 25 sm.ShowEat(); 26 sm.ShowFly(); 27 28 return 0; 29 }
②原有类:基类 父类
新类:派生类 子类
③作用:提高复用性
④父类和子类中同名的成员变量和成员函数是通过作用域来区分的
子类的大小=子类+父类
1 #include<iostream> 2 using namespace std; 3 4 class CFather 5 { 6 public: 7 int m_nMoney; 8 public: 9 CFather() 10 { 11 m_nMoney = 1000000; 12 } 13 }; 14 15 class CSon : public CFather 16 { 17 public: 18 int m_nMoney; 19 public: 20 CSon() 21 { 22 m_nMoney = 100; 23 } 24 }; 25 26 int main() 27 { 28 CFather cf; 29 CSon cs; 30 31 cout << cs.m_nMoney << endl; 32 cout << cs.CFather::m_nMoney << endl; 33 cout << sizeof(cs) << endl; //输出 8 34 35 return 0; 36 }
⑥继承方式:影响的是继承过来的访问修饰符 即子类的访问修饰符
public继承:public和protected没有变化 但是父类的private在子类中是不可访问的
protected继承:public变成protected protected不变 private依然不可访问
private继承:public和protected都变成private 所有内容不可访问
特别注意:自己的类中不可定义自己的类对象 但是定义指针可以!!!
⑦继承关系的构造和析构函数的调用顺序:
定义一个派生类对象:先执行父类构造 然后子类构造 然后子类析构 父类析构
换句话说 构造函数是从父类到子类 析构是从子类到父类
1 #include<iostream> 2 using namespace std; 3 4 class CFather 5 { 6 public: 7 CFather() 8 { 9 cout << "CFather" << endl; 10 } 11 ~CFather() 12 { 13 cout << "~CFather" << endl; 14 } 15 }; 16 17 class CSon : public CFather 18 { 19 public: 20 CSon() 21 { 22 cout << "CSon" << endl; 23 } 24 ~CSon() 25 { 26 cout << "~CSon" << endl; 27 } 28 }; 29 30 int main() 31 { 32 CSon son; 33 34 return 0; 35 }
注意:
父类的构造函数是在子类的构造函数初始化列表中调用的 默认的调用是无参的
如果父类中只有带参数 也需要在子类的构造函数初始化列表中调用带参数的
2.横向关系:依赖<关联<聚合<组合
①组合:是部分与整体的关系
它是你的一部分 缺少一部分就是不完整的
直接要求:包含对象对被包含对象的拥有 以及 包含对象与被包含对象生命期的关系
例如:笔记本电脑和笔记本电脑键盘 汽车和轮胎 人和手
②聚合:是一对多的关系
暗含了一种所属关系以及生命期的关系(有无是不一定的) 被聚合对象还可以再被别的对象关联
注意:是A中放了多个B 而不是A中放了B C D
例如:班级有很多人 部门有很多职工
③关联:
某个对象长期持有对另一对象的引用 可以随时解除关联或者进行关联
关联是没有生命周期的 可以是相互的
例如:朋友(人在出生的时候 可以在类里放一根指针 有就指向这个对象 没有就指空)
④依赖:
某个对象的功能依赖另外一个对象 被依赖的对象只作为工具使用
依赖是没有生命周期的 只是使用 不持有对它的引用
例如:人依赖空气呼吸
3.总结:
①有生命周期的:组合和聚合
②没有生命周期的:关联和依赖
三.重载操作符operator
1.重载操作符有两种方法:
①在类内:调用的对象一定在左边
②在类外:需要有符号左边和符号右边(双目运算符)
2.需要注意的地方:
①表达式都有结果 所以重载的符号需要有返回值 返回值是为了和其他符号结合
②在重载输入输出操作符的时候 输入一定要放& 否则是改变不了其中的内容的
输出无所谓(这么说未免太不负责任) 但是为了养成良好的习惯 还是都加上比较好 也方便记忆嘛
3.代码:
1 #include<iostream> 2 using namespace std; 3 4 class CNum 5 { 6 public: 7 int m_num; 8 public: 9 CNum(void) 10 { 11 } 12 13 ~CNum(void) 14 { 15 } 16 17 int operator=(int num) 18 { 19 this -> m_num = num; 20 return m_num; 21 } 22 23 int operator+(int num) 24 { 25 return this -> m_num + num; 26 } 27 28 int operator++() 29 { 30 m_num = m_num + 1; 31 return m_num; 32 } 33 34 int operator++(int aaa) 35 { 36 aaa = m_num; 37 m_num = m_num + 1; 38 return aaa; 39 } 40 }; 41 42 int operator+(int num,CNum& cn) 43 { 44 return num + cn.m_num; 45 } 46 int operator+(CNum& cn1,CNum& cn2) 47 { 48 return cn1.m_num + cn2.m_num; 49 } 50 51 istream& operator>>(istream& is,CNum& cn) 52 { 53 cin >> cn.m_num; 54 return is; 55 } 56 57 ostream& operator<<(ostream& os,CNum& cn) 58 { 59 cout << cn.m_num; 60 return os; 61 } 62 63 int main() 64 { 65 CNum a; 66 a = 100; 67 cout << a.m_num << endl; //100 68 69 CNum b; 70 b = a = 200; 71 cout << a.m_num << endl; //200 72 cout << b.m_num << endl; //200 73 74 CNum c; 75 c = a + 100; 76 cout << c.m_num << endl; //200+100=300 77 78 CNum d; 79 d = 100 + a; 80 cout << d.m_num << endl; //100+200=300 81 82 CNum e; 83 e = a + b; 84 cout << e.m_num << endl; //200+200=400 85 86 cin >> a; 87 cout << a << endl; 88 89 return 0; 90 }