简述一下什么是面向对象
1、面向对象是一种编程思想,把一切东西看成一个个对象。把这些类拥有的属性变量和操作这些属性变量的函数打包成一个类。
2、面向过程和面向对象区别
面向过程:根据业务逻辑从上到下编写代码
面向对象:将数据和函数进行封装,可以快速开发,减少重复代码的重写。
简述一下面向对象的三大特征
封装、继承、多态
1、封装
将数据和操作数据方法进行结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。封装的本质就是管理。
2、继承
可以使用现有类的所有功能,并在不需要重写原理类的情况下进行功能扩展。
继承方式:
继承方式 | private继承 | protected继承 | public继承 |
基类的private成员 | 不可见 | 不可见 | 不可见 |
基类的protected成员 | 变为private成员 | 仍为protected成员 | 仍为protected成员 |
基类的public成员 | 变为private成员 | 变为protected成员 | 仍为public成员 |
3、多态
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。就是用基类的引用指向子类的对象。
多态实现:重写、重载
简述一下 C++ 的重载和重写,以及它们的区别
1、重写
指派生类中存在重新定义的函数。其函数名、参数列表、返回值类型,所有都必须同基类中被重写的函数一致,派生类对象调用时会调用派生类的重写函数,不会调用被重写函数。
重写的基类中被重写的函数必须有virtual修饰。
示例:
#include<bits/stdc++.h> using namespace std; class A { public: virtual void fun() { cout << "A"; } }; class B :public A { public: virtual void fun() { cout << "B"; } }; int main(void) { A* a = new B(); a->fun();//输出B,A类中的fun在B类中重写 }
2、重载
我们在平时写代码中会用到几个函数但是他们的实现功能相同,但是有些细节却不同。在 C++中人们提出了用一个函数名定义多个函数,也就是所谓的函数重载。
函数重载是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
#include<bits/stdc++.h> using namespace std; class A { void fun() {}; void fun(int i) {}; void fun(int i, int j) {}; void fun1(int i,int j){}; };
C++ 的重载和重写是如何实现的
1、C++利用命名倾轧(name mangling)技术,来改名函数名,区分参数不同的同名函数。命名倾轧是在编译阶段完成的。
2、在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。
对象类型是派生类,就调用派生类的函数;对象类型是基类,就调用基类的函数。
- 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
- 存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
- 多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
- 重写用虚函数来实现,结合动态绑定。
- 纯虚函数是虚函数再加上 = 0。
-
抽象类是指包括至少一个纯虚函数的类。
纯虚函数:virtual void fun()=0。即抽象类必须在子类实现这个函数,即先有名称,没有内容,在派生类实现内容。
C 语言如何实现 C++ 语言中的重载
c语言中不允许有同名函数,因为编译时函数命名是一样的,不像c++会添加参数类型和返回类型作为函数编译后的名称,进而实现重载。如果要用c语言显现函数重载,可通过以下方式来实现:
1、使用函数指针来实现,重载的函数不能使用同名称,只是类似的实现了函数重载功能。
2、重载函数使用可变参数,方式如打开文件open函数。
3、gcc有内置函数,程序使用编译函数可以实现函数重载
示例:
#include<stdio.h> void func_int(void * a) { printf("%d\n",*(int*)a); //输出int类型,注意 void * 转化为int } void func_double(void * b) { printf("%.2f\n",*(double*)b); } typedef void (*ptr)(void *); //typedef申明一个函数指针 void c_func(ptr p,void *param) { p(param); //调用对应函数 } int main() { int a = 23; double b = 23.23; c_func(func_int,&a); c_func(func_double,&b); return 0; }
构造函数有几种,分别什么作用
C++中的构造函数可以分为4类:默认构造函数、初始化构造函数、拷贝构造函数、移动构造函数。
1、默认构造和初始化构造。定义类的对象时完成初始化工作。
示例:
class Student { public: //默认构造函数 Student() { num=1001; age=18; } //初始化构造函数 Student(int n,int a):num(n),age(a){} private: int num; int age; }; int main() { //用默认构造函数初始化对象S1 Student s1; //用初始化构造函数初始化对象S2 Student s2(1002,18); return 0; }
有有参构造,编译器不再提供默认的构造函数。
2、拷贝构造
#include "stdafx.h" #include "iostream.h" class Test { int i; int *p; public: Test(int ai,int value) { i = ai; p = new int(value); } ~Test() { delete p; } Test(const Test& t) { this->i = t.i; this->p = new int(*t.p); } }; //复制构造函数用于复制本类的对象 int main(int argc, char* argv[]) { Test t1(1,2); Test t2(t1);//将对象t1复制给t2。注意复制和赋值的概念不同 return 0; }
3、移动构造函数。用于将其他类型的变量,隐式转换为本类对象。下面的转换构造函数,将int类型的r转换为Student类型的对象,对象的age为r,num为1004。
Student(int r) { int num=1004; int age= r; }
定义一个空类,默认会生成哪些函数
无参的构造函数、拷贝构造函数、赋值运算符、析构函数(非虚)。
C++ 类对象的初始化顺序,有多重继承情况下的顺序
- 创建派生类的对象,基类的构造函数优先被调用(也优先于派生类里的成员类)
- 如果类里面有成员类,成员类的构造函数优先被调用;(也优先于该类本身的构造函数)
- 基类构造函数如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序而不是它们在成员初始化表中的顺序
- 成员类对象构造函数如果有多个成员类对象,则构造函数的调用顺序是对象在类中被声明的顺序而不是它们出现在成员初始化表中的顺序
- 派生类构造函数,作为一般规则派生类构造函数应该不能直接向一个基类数据成员赋值而是把值传递给适当的基类构造函数,否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。(基类设计者的责任是提供一组适当的基类构造函数)
总结:
父类构造函数–>成员类对象构造函数–>自身构造函数
中成员变量的初始化与声明顺序有关,构造函数的调用顺序是类派生列表中的顺序
析构顺序和构造顺序相反