C++面向对象的三大特性为:封装、继承、多态
C++认为万事万物都皆为对象,对象上有其属性和行为

4.1 封装

4.1.1封装的意义

封装的意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

定义类,类的实例

  1. 在设计类的时候,属性和行为写在一起,表现事物<br />**语法:**class 类名{ 访问权限: 属性 / 行为 };

示例1:设计一个圆类,求圆的周长

  1. //圆周率
  2. const double PI = 3.14;
  3. //1、封装的意义
  4. //将属性和行为作为一个整体,用来表现生活中的事物
  5. //封装一个圆类,求圆的周长
  6. //class代表设计一个类,后面跟着的是类名
  7. class Circle
  8. {
  9. public: //访问权限 公共的权限
  10. //属性
  11. int m_r;//半径
  12. //行为
  13. //获取到圆的周长
  14. double calculateZC()
  15. {
  16. //2 * pi * r
  17. //获取圆的周长
  18. return 2 * PI * m_r;
  19. }
  20. };
  21. int main() {
  22. //通过圆类,创建圆的对象,实例化
  23. // c1就是一个具体的圆
  24. Circle c1;
  25. //通过.语法可以访问到对象里的方法和值;
  26. c1.m_r = 10; //给圆对象的半径 进行赋值操作
  27. //2 * pi * 10 = = 62.8
  28. cout << "圆的周长为: " << c1.calculateZC() << endl;
  29. system("pause");
  30. return 0;
  31. }

设计类的权限

类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:

  1. public 公共权限 类内可以访问 类外可以访问
  2. protected 保护权限 类内可以访问 类外不可以访问,但是子类可以访问
  3. private 私有权限 类内可以访问 类外不可以访问 ```cpp //三种权限 //公共权限 public 类内可以访问 类外可以访问 //保护权限 protected 类内可以访问 类外不可以访问 //私有权限 private 类内可以访问 类外不可以访问

class Person { //姓名 公共权限 public: string m_Name;

  1. //汽车 保护权限

protected: string m_Car;

  1. //银行卡密码 私有权限

private: int m_Password;

public: void func() { m_Name = “张三”; m_Car = “拖拉机”; m_Password = 123456; } };

int main() {

  1. Person p;
  2. p.m_Name = "李四";
  3. //p.m_Car = "奔驰"; //保护权限类外访问不到
  4. //p.m_Password = 123; //私有权限类外访问不到
  5. system("pause");
  6. return 0;

}

  1. <a name="JCOLd"></a>
  2. ### 4.1.2 struct 结构体和class区别
  3. 在C++中 struct和class唯一的**区别**就在于 **默认的访问权限不同**<br />区别:
  4. - **struct 默认权限为公共**
  5. - **class 默认权限为私有**
  6. ```cpp
  7. //定义类
  8. class C1
  9. {
  10. int m_A; //默认是私有权限
  11. };
  12. //定义结构体
  13. struct C2
  14. {
  15. int m_A; //默认是公共权限
  16. };
  17. int main() {
  18. //类的实例
  19. C1 c1;
  20. c1.m_A = 10; //错误,访问权限是私有
  21. //结构体的实例
  22. C2 c2;
  23. c2.m_A = 10; //正确,访问权限是公共
  24. system("pause");
  25. return 0;
  26. }

4.13 成员属性设置为私有

优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
定义类的时候,
在公共权限中设置,函数来获取或者设置属性;
私有权限中创建,属性;
通过类中的函数来实现,属性是否可读,可写;

  1. class Person {
  2. public:
  3. //姓名设置可读可写
  4. void setName(string name) {
  5. m_Name = name;
  6. }
  7. string getName()
  8. {
  9. return m_Name;
  10. }
  11. //获取年龄
  12. int getAge() {
  13. return m_Age;
  14. }
  15. //设置年龄
  16. void setAge(int age) {
  17. if (age < 0 || age > 150) {
  18. cout << "你个老妖精!" << endl;
  19. return;
  20. }
  21. m_Age = age;
  22. }
  23. //情人设置为只写
  24. void setLover(string lover) {
  25. m_Lover = lover;
  26. }
  27. private:
  28. string m_Name; //可读可写 姓名
  29. int m_Age; //只读 年龄
  30. string m_Lover; //只写 情人
  31. };
  32. int main() {
  33. Person p;
  34. //姓名设置
  35. p.setName("张三");
  36. cout << "姓名: " << p.getName() << endl;
  37. //年龄设置
  38. p.setAge(50);
  39. cout << "年龄: " << p.getAge() << endl;
  40. //情人设置
  41. p.setLover("苍井");
  42. //cout << "情人: " << p.m_Lover << endl; //只写属性,不可以读取
  43. system("pause");
  44. return 0;
  45. }

4.2 对象的初始化和清理

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。

    4.2.1 构造函数 和 析构函数

    对象的初始化和清理也是两个非常重要的安全问题
    一个对象或者变量没有初始状态,对其使用后果是未知
    同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

c++利用了构造函数析构函数解决上述问题,
这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现(函数里都是空的什么都没写)。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数(初始化)

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用(创建)对象时候会自动调用构造,无须手动调用,而且只会调用一次

    析构函数(清理)

    析构函数语法:~类名(){}

  5. 析构函数,没有返回值也不写void

  6. 函数名称与类名相同,在名称前加上符号 ~
  7. 析构函数不可以有参数,因此不可以发生重载
  8. 程序在对象销毁会自动调用析构,无须手动调用,而且只会调用一次
  1. class Person
  2. {
  3. public:
  4. //构造函数
  5. //如果不写构造和析构,系统会自动写一个空的;
  6. Person()
  7. {
  8. cout << "Person的构造函数被调用" << endl;
  9. }
  10. //析构函数
  11. ~Person()
  12. {
  13. cout << "Person的析构函数被调用" << endl;
  14. }
  15. };
  16. void test01()
  17. {
  18. Person p;
  19. }
  20. int main() {
  21. //调用函数test01,来创建对象P
  22. //这时候会调用构造和析构,因为函数里创建的P是一个局部变量,函数调用完就释放
  23. test01();
  24. //这里虽然也是在函数中创建对象P,但是只调用了构造函数,没有调用析构函数;
  25. //因为下面的 system("pause"); 把main函数停止了,main并没有实行完毕
  26. //如果在dos中,按下了回车,就会调用析构函数;
  27. Person p;
  28. system("pause");
  29. return 0;
  30. }

4.2.2 构造函数的分类及调用

两种分类方式:
按参数分为: 有参和无参构造
Person(){}
Person(int a){}
按类型分为: 普通构造和拷贝构造
Person(const Person& p){}

三种调用方式:
括号法
有参构造调用
Person p1 (10);
拷贝构造调用
Person p2 (p1);

  1. **显示法**<br />有参构造调用<br />**Person p3 = Person(10); **<br />拷贝构造调用<br />**Person p4 = Person(p1); **
  2. **隐式转换法**<br />有参构造调用<br /> // Person p4 = Person(10); <br /> Person p6 = 10; <br />拷贝构造调用<br /> // Person p5 = Person(p4); <br /> Person p7 = p4;

注意事项
调用无参构造函数,不要使用(),;编译器会认为这是一个函数声明;
//不能使用()
//编译器认为是
错误 Person p2();
//调用无参构造
Person p2;
不要调用拷贝构造函数,初始化匿名对象;
Person p1;
//调用拷贝构造,创建匿名对象
//编辑器会认为是 Person(p1) === Person p1
错误 Person (p1);

  1. class Person {
  2. public:
  3. //无参构造函数
  4. Person() {
  5. cout << "无参构造函数被调用!" << endl;
  6. }
  7. //有参构造函数
  8. Person(int a) {
  9. age = a;
  10. cout << "有参构造函数被调用!" << endl;
  11. }
  12. //拷贝构造函数
  13. Person(const Person& p) {
  14. age = p.age;
  15. cout << "拷贝构造函数被调用!" << endl;
  16. }
  17. //析构函数
  18. ~Person() {
  19. cout << "析构函数被调用!" << endl;
  20. }
  21. public:
  22. int age;
  23. };
  24. //创建对象p函数
  25. void test01() {
  26. //创建对象P
  27. //这是一个局部变量
  28. //没有传参,调用了无参构造函数;
  29. Person p;
  30. }
  31. void test02() {
  32. //括号法,也就是传参;
  33. //创建P1对象,传参10,调用有参构造函数
  34. Person p1(10);
  35. //创建对象p2,传参P1对象,调用了拷贝构造函数
  36. Person p2(p1);
  37. //注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
  38. 错误--Person p2();
  39. // 显式法
  40. //调用了有参构造,传参10
  41. Person p3 = Person(10);
  42. //调用了拷贝构造
  43. Person p4 = Person(p2);
  44. //创建了一个匿名对象,当前行结束之后,马上析构
  45. Person(10)
  46. //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
  47. //编译器认为Person(p4) === Person p4
  48. 错误--Person(p4);
  49. //隐式转换法
  50. // Person p4 = Person(10);
  51. Person p6 = 10;
  52. // Person p5 = Person(p4);
  53. Person p7 = p4;
  54. }
  55. int main() {
  56. test01();
  57. test02();
  58. system("pause");
  59. return 0;
  60. }

4.2.3 拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象