当前位置:   article > 正文

C++学习笔记 (二)面向对象:封装、继承、多态_2、c++面向对象封装、继承

2、c++面向对象封装、继承

1.内存

分四区(意义:对不同区域的数据,赋予不同的生命周期,给我们更大的灵活编程)

(1)代码区:存放二进制代码,由操作系统管理

(2)全局区:存放全局变量、静态变量、常量(该区数据由系统释放)

(3)堆区:由人分配、释放。若人没手动释放,则结束时会由操作系统回收(用new在堆区开辟内存,用delete释放)

 

new返回的是该类型数据的指针,如int* p = new int(10);

 

 

 

 

 

 

(4)栈区:由编译器自动分配、释放,存放函数参数、局部变量等

 

2.引用

作用:用来给变量起别名

数据类型 &别名 = 原名;

 

(1)用引用,必须要初始化,且之后就不能更改了

 

以上输出的a、b、c都是20 。

 

(2)引用做函数参数

作用:函数传参时,可以用引用的技术让形参修饰实参

优势:与地址传递的效果一样,其更好理解。

 

(3)引用做函数的返回值

 

(4)引用的本质是指针常量(指针的指向不可改,指向的值可改)

 

(5)常量引用

 

3.函数

(1)默认参数

 

(2)占位参数

 

(3)函数重载

作用:函数名可以相同,提高复用性

需满足3个条件:

①同一个作用域   

②函数名称相同       

③参数类型不同或个数不同或顺序不同

(函数返回值不能作为函数重载的条件)

函数重载注意事项:

 

4.类和对象

面向对象的3大特性:封装、继承、多态

c++认为万事万物皆为对象,有属性与行为。

二者关系:把具有相同性质的对象,可以抽象称之为类

(1)封装的意义:类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限有3种:

public 公共权限,成员类内外都可访问;

protected 保护权限,成员类内可访问(同一个class内),类外不行,儿子(如A:public B)可以访问父亲中的保护内容

private 私有权限,成员类内可访问,类外不行,儿子不能访问父亲中的保护内容;

注:

C++ 访问权限问题主要包括两种:一种是外界(类外)对类成员的普通访问,通过类内的public;另一种是继承关系中子类对父类成员的访问。

基本原则:外界只能访问类中public成员,子类可访问父类的public和protected成员;不同继承方式只影响外界(包括子类的子类)通过子类对父类成员的访问权限。

一、 外界对类成员的普通访问

1. 当类成员在类中为public权限时,才可在类外访问,其余不可。

2. 若存在继承关系,从父类继承来的成员若在该类中仍具有public权限,也可在类外访问,其余不可。
二、继承关系中子类对父类成员的访问

1. 无论通过什么方式(public、protected、private)继承,在子类内部均可访问父类中的public、protected成员,private成员不可访问。

    注意:

      继承方式只影响外界(包括子类的子类)通过子类对父类成员的访问权限。

      public继承,父类成员的访问权限全部保留至子类;

      protected继承,父类public成员的访问权限在子类中降至protected;

      private继承,父类public、protected成员的访问权限在子类中均降至private。

2. 父类的析构函数若声明为protected (无论有无virutal),外界均不可调用delete 父类指针;因为是protected权限,子类析构后会自动调用父类析构函数。

   这种情况下,最好不要在父类成员中有动态内存分配。

3. 通过protected/private继承的子类,不能通过static_cast/dynamic_cast向父类转换;只能通过reinterpret_cast引用或指针的方式强制转换,按照父类内存结构重新解释,可改变成员的访问权限。

举例

可见以上目前存在私有的访问权限问题,解决方法有两种:

①  改为成员函数,父类的成员函数是可以访问自己的私有成员的

②  改为友元函数,可以在类外访问私有变量

 

(2)同样表示的是类,struct与class唯一的区别在于默认的访问权限不同:struct默认公有,class默认私有

 

(3)成员属性设置为私有的好处:可以自己控制读写权限。且对于写权限,我们可以检测数据的有效性。

         再通过类内设置的get()、set()方法来读取或改写私有属性

 

  1. #include <iostream>
  2. using namespace std;
  3. //创建立方体类
  4. //设计属性
  5. //设计行为 获取立方体面积和体积
  6. //分别利用全局函数和成员函数 判断两个立方体是否相同
  7. class Cube
  8. {
  9. int m_L;
  10. int m_W;
  11. int m_H;
  12. public:
  13. void setL(int l) //设置长
  14. {
  15. m_L = l;
  16. }
  17. int getL() //获取长
  18. {
  19. return m_L;
  20. }
  21. void setW(int w)
  22. {
  23. m_W = w;
  24. }
  25. int getW()
  26. {
  27. return m_W;
  28. }
  29. void setH(int h)
  30. {
  31. m_H = h;
  32. }
  33. int getH()
  34. {
  35. return m_H;
  36. }
  37. int calculateS()
  38. {
  39. return 2*(m_L*m_W + m_L*m_H + m_W*m_H);
  40. }
  41. int calculateV()
  42. {
  43. return m_L*m_W*m_H;
  44. }
  45. //用成员函数判断两个立方体是否相同
  46. bool isSameByClass(Cube &c)
  47. {
  48. if(m_L==c.getL() && m_W==c.getW() && m_H==c.getH())
  49. return true;
  50. return false;
  51. }
  52. };
  53. bool isSame(Cube &c1, Cube &c2);
  54. int main()
  55. {
  56. Cube c1;
  57. c1.setL(10);
  58. c1.setW(10);
  59. c1.setH(10);
  60. cout << "c1面积: " << c1.calculateS() << endl;
  61. cout << "c1体积: " << c1.calculateV() << endl;
  62. Cube c2;
  63. c2.setL(10);
  64. c2.setW(10);
  65. c2.setH(11);
  66. if(isSame(c1, c2))
  67. cout << "全局函数判断: c1和c2相同 \n";
  68. else
  69. cout << "全局函数判断: c1和c2不同 \n";
  70. if(c1.isSameByClass(c2))
  71. cout << "成员函数判断: c1和c2相同 \n";
  72. else
  73. cout << "成员函数判断: c1和c2不同 \n";
  74. return 0;
  75. }
  76. //用全局函数判断两个立方体是否相同
  77. bool isSame(Cube &c1, Cube &c2)
  78. {
  79. if(c1.getL()==c2.getL() && c1.getW()==c2.getW() && c1.getH()==c2.getH())
  80. return true;
  81. return false;
  82. }

  1. #include <iostream>
  2. using namespace std;
  3. //点类
  4. class Point
  5. {
  6. int m_X;
  7. int m_Y;
  8. public:
  9. void setX(int x)
  10. {
  11. m_X = x;
  12. }
  13. int getX()
  14. {
  15. return m_X;
  16. }
  17. void setY(int y)
  18. {
  19. m_Y = y;
  20. }
  21. int getY()
  22. {
  23. return m_Y;
  24. }
  25. };
  26. class Circle
  27. {
  28. int m_R;
  29. Point m_Center;
  30. public:
  31. void setR(int r)
  32. {
  33. m_R = r;
  34. }
  35. int getR()
  36. {
  37. return m_R;
  38. }
  39. void setCenter(Point center)
  40. {
  41. m_Center = center;
  42. }
  43. Point getCenter()
  44. {
  45. return m_Center;
  46. }
  47. };
  48. //判断点与圆的关系
  49. void isInCircle(Circle &c, Point &p)
  50. {
  51. //c.getCenter()返回的m_Center是Point类,所以可以直接调用Point的getX()、getY()方法。
  52. int Distance =
  53. (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX())
  54. + (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
  55. int rDistance = c.getR() * c.getR();
  56. if(Distance == rDistance)
  57. cout << "点在圆上\n";
  58. else if (Distance > rDistance)
  59. cout << "点在圆外\n";
  60. else
  61. cout << "点在圆内\n";
  62. }
  63. int main()
  64. {
  65. Circle c;
  66. c.setR(10);
  67. Point center;
  68. center.setX(10);
  69. center.setY(0);
  70. c.setCenter(center);
  71. Point p;
  72. p.setX(10);
  73. p.setY(11);
  74. isInCircle(c, p);
  75. return 0;
  76. }

(4)构造函数用于初始化,析构函数用于清理

 

 

 

 

以上能说明,在调用对象时会自动地调用构造与析构函数。

 

(5)构造函数分类及调用

 

① 构造函数分类

 

② 构造函数调用

函数调用的3种方法: 

 

(6)拷贝构造函数调用时机

 

(7)构造函数的调用规则

默认情况下,c++的每个类至少包含3个函数,构造、析构和拷贝构造函数

 

 

(8)深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝

深拷贝:在·堆区重新申请空间,做拷贝操作

注:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,以防止浅拷贝带来的问题。

 

(9)初始化列表 

 

 

(10)类对象做为类成员

类里的成员可以是另一个类的对象,称此成员为对象成员

 

 

(11)静态成员:变量,函数

静态成员函数只能访问静态成员变量:

 

静态成员变量有两种访问方式:

 

(12)类内的成员变量与成员函数是分开存储的,只有非静态成员变量才属于类的对象上。

c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置,每个空对象都具有独一无二的内存地址。

(13)this指针!!!(和&引用一样,都是指向不可变)

特殊的对象指针,用来指向被调用的成员函数所属的对象。本质是指针常量,其指向不可改。

如上图,因为this是指针,其指向对象p1,则解引用*this得到p1,即返回了对象本身。

作用:

①解决名称冲突(当形参和成员变量同名时,可以用this指针做区分)

②返回对象本身用*this

可见并没有完成赋值,原因就是形参和成员变量名重复了,此时就可以用this指针解决:

 

(14)空指针访问成员函数

报错如下:

修改如下,以保证代码健壮性:

 

(15)常函数与常对象!!!

成员函数之后加const,称为常函数,其内不可修改成员属性。但当成员属性在声明时加了关键字mutable后,则可修改。

声明对象前加const,称为常对象,常对象只能调用常函数

注:在成员函数后加const,修饰的是this指针,令其指向的(this指针作为指针常量,其指向不可改,指向的值可改)也不可能改了。

 

 

注:

const char* ptr == char const* ptr;  (可以直接改变指针指向,但不能直接改变指针指向的值);*ptr=*ss;

char* const ptr; (可以直接改变指针指向的值,但不能直接改变指针指向);ptr[0]='s';

但两者都可以通过改变所指向的指针的内容,来改变它的值。

const char* const ptr;(指向和指向的值都不可改)。

例1

例2

例3

(16)友元 friend

让类外的一些函数或类,能够访问该类的私有成员!!!

3种实现:全局函数做友元,类做友元,成员函数做友元

1)全局函数做友元

 

 

2)类做友元

 

 

3)成员函数做友元

 

 

(17)运算符重载

对已有的运算符做重新定义,赋予其另一种功能,以适应不同的数据类型

 

1)加号运算符重载:用成员函数实现,用全局函数实现

实现两个自定义数据类型相加

①用成员函数实现

 

②用全局函数实现

 

③重载运算符函数的重载

 

2)左移运算符重载

重载左移运算符配合友元,可以实现输出自定义数据类型

 

3)递增运算符重载

4)赋值运算符重载

5)关系运算符重载

6)函数调用运算符重载

函数调用运算符()也可以重载。由于重载后使用的方式非常像函数的调用,故称为仿函数。

仿函数没有固定写法,非常灵活。

 

5.继承

(1)继承的方式

 

 

(2)继承中的对象模型

 

(3)继承中的构造与析构顺序

 

(4)继承中,同名成员的处理方式

 

 

 

 

(5)继承中,同名静态成员的处理方式

 

(6)多继承

 

(3)菱形继承

菱形继承带来的问题是子类会同时继承两份相同的数据,导致资源浪费以及毫无意义。可以用虚继承来解决这个问题。

 

 

 

6.多态

(1)多态的基本语法

如果想执行让猫说话,那么这个函数地址就不能提前绑定,而是要在运行阶段才绑定,要借助虚函数来实现

注:重写不是重载。重写是指函数的返回值类型、函数名、参数列表都完全一致

 

(2)

(3)纯虚函数与抽象类

 

 

 

举例:

   

 

(4)虚析构与纯虚析构

举例:

 

 

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

闽ICP备14008679号