深入理解面向对象编程思想及其三大特性(封装、继承、多态)
C语言与C++的区别
- 头文件不同,C语言的头文件有.h,C++中没有
- C语言中没有Bool 类型
- C语言不能函数重载,C++可以进行函数重载
- 什么是函数重载?
在一个作用域内,函数名字相同,参数的个数顺序或者类型其中一个不同则称为函数重载,函数重载与返回值无关
C++为什么能够函数重载?
因为C++的编译器在编译函数的时候会将函数名原本的名字加上参数类型来识别不同的重载函数,但是C语言并没有这样的功能。因此语言不能函数重载
- C语言是面向过程编程,C++是面向对象编程
- NULL在C语言中是(void*)0 ,而在C++中是0。原因是在C语言中允许(void*)指针隐式转换为任何其他类型的指针,但是C++是比C语言更加严格的,是不允许这样的转换的,因此在C++中引入了nullptr代表空指针,这样就避免了歧义
三大特性(封装、继承、多态)
说到面向对象编程,就要讲到对象的三大特性:封装,继承和多态。
封装
将事物属性(成员变量)和行为(成员函数)封装在一起形成一个类。并且可以设置访问权限
class` `People` `{`
`public:`
` string name;`
`void` `eat();`
`private:`
` string sex;`
`};
- 面向对象编程中的权限包括:
- public:公共权限,所有类都可以访问
- private:私有权限,只有当前类可以访问
- protected:受保护权限,只有当前类以及子类可以访问
- 封装的原则:
对所有的成员变量使用private权限,并使用成员函数set和get对变量进行读写操作,可以防止成员变量暴露
继承
用已经存在的类去实现新的类,相当于对现有的类进行扩展,提高了代码的复用性,可维护性,是多态的前提
class 派生类名:继承方式 基类的名称`
`class` `A :public B
继承分为:单继承和多继承,单继承:只有一个父类,多继承:拥有多个父类
多继承可能会导致菱形继承
菱形继承:B和C同时继承了A,所以BC中都拥有A中的共同属性,那如果此时D创建一个对象,初始化这个共同属性,此时会导致编译器没有办法去辨认这个公共属性属于哪一个类
#include <iostream>`
`using` `namespace std;`
`class` `A` `{`
`public:`
`int value;`
`};`
`class` `B` `:` `public A` `{};`
`class` `C` `:` `public A` `{};`
`class` `D` `:` `public B, public C` `{};`
`int` `main()` `{`
` D d;`
` d.value =` `10;` `// 错误:二义性,无法确定是 B::value 还是 C::value`
` cout << d.value << endl;`
`return` `0;`
`}

菱形继承的解决办法:
- 可以添加作用域:在这个公共属性前面添加作用域,此时编译器就能够区别
- 可以采用虚继承:在继承方式的前面加上virtual
C++ 提供了虚继承的机制来解决菱形继承问题。虚继承确保在菱形继承结构中,最底层的派生类(如 D)只保留一份基类(如 A)的成员,从而消除二义性。
#include <iostream>`
`using` `namespace std;`
`class` `A` `{`
`public:`
`int value;`
`};`
`class` `B` `:` `virtual public A` `{};` `// 虚继承`
`class` `C` `:` `virtual public A` `{};` `// 虚继承`
`class` `D` `:` `public B, public C` `{};`
`int` `main()` `{`
` D d;`
` d.value =` `10;` `// 正确:只有一份 A::value`
` cout << d.value << endl;`
`return` `0;`
`}
虚继承的工作原理
虚继承通过引入一个虚基类表(Virtual Base Table)来实现。在虚继承的情况下:
- 编译器会为每个虚继承的类生成一个虚基类表。
- 最底层的派生类(如 D)会直接指向虚基类(如 A)的成员,而不是通过中间类(如 B 和 C)间接访问。
- 这样,无论 D 通过 B 还是 C 访问 A 的成员,都会指向同一份数据。
多态
不同的类对同一件事(即方法的调用)做出不同的响应,从而增强了代码的灵活性和可扩展性。
多态分为静态多态和动态多态
- 静态多态(编译时多态):通过函数重载和运算符重载实现
- 动态多态(运行时多态):通过虚函数和继承实现,父类指针或引用指向子类对象,并通过指针或引用调用重写函数
(1)虚函数(Virtual Function)
虚函数是在基类中使用 virtual 关键字声明的函数,派生类可以重写(Override)这些函数。通过基类指针或引用调用虚函数时,实际调用的是派生类中重写的函数。
#include <iostream>`
`using` `namespace std;`
`class` `Animal` `{`
`public:`
`virtual` `void` `speak()` `{` `// 虚函数`
` cout <<` `"Animal speaks"` `<< endl;`
`}`
`};`
`class` `Dog` `:` `public Animal` `{`
`public:`
`void` `speak()` `override` `{` `// 重写虚函数`
` cout <<` `"Dog barks"` `<< endl;`
`}`
`};`
`class` `Cat` `:` `public Animal` `{`
`public:`
`void` `speak()` `override` `{` `// 重写虚函数`
` cout <<` `"Cat meows"` `<< endl;`
`}`
`};`
`int` `main()` `{`
` Animal* animal1 =` `new` `Dog();`
` Animal* animal2 =` `new` `Cat();`
` animal1->speak();` `// 输出: Dog barks`
` animal2->speak();` `// 输出: Cat meows`
`delete animal1;`
`delete animal2;`
`return` `0;`
`}
(2)纯虚函数和抽象类
如果基类中的虚函数没有实现,而是由派生类必须实现,那么这种虚函数称为纯虚函数 。包含纯虚函数的类称为抽象类 ,抽象类不能实例化。
#include <iostream>`
`using` `namespace std;`
`class` `Shape` `{`
`public:`
`virtual` `void` `draw()` `=` `0;` `// 纯虚函数`
`};`
`class` `Circle` `:` `public Shape` `{`
`public:`
`void` `draw()` `override` `{`
` cout <<` `"Drawing Circle"` `<< endl;`
`}`
`};`
`class` `Square` `:` `public Shape` `{`
`public:`
`void` `draw()` `override` `{`
` cout <<` `"Drawing Square"` `<< endl;`
`}`
`};`
`int` `main()` `{`
` Shape* shape1 =` `new` `Circle();`
` Shape* shape2 =` `new` `Square();`
` shape1->draw();` `// 输出: Drawing Circle`
` shape2->draw();` `// 输出: Drawing Square`
`delete shape1;`
`delete shape2;`
`return` `0;`
`}`
`