一.对象的种类
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.总结:
有生命周期的:组合和聚合
没有生命周期的:关联和依赖