作者:七月在线 七仔
链接:https://zhuanlan.zhihu.com/p/451245319
来源:知乎
一、C++语言
1、如何理解C++的多态?可以给出一个示例程序证明吗?
https://blog.csdn.net/m0_57706881/article/details/124542611
例如:去游乐场玩,儿童买票是半价,成年人买票是全价,老年人买票打七折。 对比类的话,这个买票就是一个类的一个方法(函数)。儿童,成年人,老年人即是对象,这些对象调用同一个买票函数得出来的不同的票价,这就是多态。
多态,即多种形态,不同对象收到相同的的消息时(即调用相同的函数),产生不同的动作。直观的说,多态性是指用一个名字定义不同的函数(这里只要函数名相同就行),这些函数执行不同但又类似的操作,从而可以使用相同的方式来调用这些具有不同功能的同名函数。
实例代码
#include
using namespace std;
//将每个英雄的共同特征抽象出来形成一个抽象类
class hero{
public:
virtual void blood()=0;
};
//A英雄继承了抽象类实现了抽象类里面的方法
class A:public hero{
public:
void blood()
{
cout<<"A的血量减一"<<endl;
}
};
//A英雄继承了抽象类实现了抽象类里面的方法
class B:public hero{
public:
void blood()
{
cout<<"B的血量减一"<<endl;
}
};
//tower相当于防御塔类,英雄进塔会遭到攻击
class tower{
public:
/*当通过继承实现多态的时候,函数的参数类型只需要定义为父类的指针类型 。
当攻击具体英雄的时候只需要传入具体对象的指针,然后就可以调用不同对象的掉血函数了
后面无论增加多少个新的英雄,这个函数都不用变。*/
void beat(hero *p)
{
p->blood();
}
/* 如果不通过继承实现多态的话,后期每增加一个英雄,防御都要增加一个对应的掉血函数。
void beat(A *p)
{
p->blood();
}
void beat(B *p)
{
p->blood();
}
*/
};
int main()
{
A a,*pc;
B b;
tower t;
t.beat(&a);//防御塔攻击a
t.beat(&b);//防御塔攻击b
}
运行结果:
A的血量减一
B的血量减一
了解早绑定(静态绑定)和晚绑定(动态绑定)吗?
静态多态和动态多态区别其实只是在什么时候将函数实现和函数调用关联起来,是在编译时期还是运行时期,即函数地址是早绑定还是晚绑定。静态多态是指在编译期间就可以确定函数的调用地址,并产生代码,这是静态的,也就是说地址是早绑定。静态多态也被叫做静态联编。动态多态是指函数在运行期间才能确定函数的调用地址,这是动态的,也就是说地址是晚绑定。
————————————————
(1)多态的成立条件…
https://blog.csdn.net/a1367666195/article/details/125658123
①首先,要有继承关系,即,发生在基类和派生类之间;
②基类和派生类要有相同函数,这个相同,指得是函数名、输入形参、输出都要一样,比重载和遮蔽要求都要高;
③在基类成员函数前面添加virtual关键字。
(2)多态基本概念…
多态是面向对象程序设计的一个重要特征。多态的字面意思就是多种状态,在面向对象的程序设计中,一个接口,多种实现即为多态。c++的多态性具体体现在编译和运行两个阶段。编译时多态是静态多态,在编译时就可以确定使用的接口。运行时多态是动态多态,具体引用的接口在运行时才能确定。
(4)如何实现动态绑定的机制?…
动态多态是通过虚函数和类的继承来实现的。动态多态需要满足如下条件:
.(5)多态原理的实质?…
(6)向上类型转换和向下类型转换?…
将派生类指针或引用转换为基类的指针或引用被称为向上类型转换",将基类指针或引用转换为派生类指针或引用被称为向下类型转换。
(7)虚函数和纯虚函数之间的区别?
3.虚函数和纯虚函数都可以在子类中被重载,以多态的形式被调用。
4.虚函数和纯虚函数通常存在于抽象基类之中,被继承的子类重载,目的是提供一个统一的接口。
5.虚函数的定义形式:virtual { };纯虚函数的定义形式:virtual { } = 0;在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时要求前期绑定,然而虚函数却是动态绑定,而且被两者修饰的函数生命周期也不一样。
————————————————
2、为什么C++要在头文件中声明,在源文件中定义?…
看起来把函数的声明直接放在使用该函数的源文件中是合法的,也比较容易被人接受,但这么做可能会很繁琐且易出错,相反如果把函数声明放在头文件中,就能确保统一函数的所有声明保持一致,而且一旦我们想改变函数的接口,只需要改变一条声明即可。
定义函数的源文件应该把含有函数声明的头文件包含进来,编译器负责验证函数的定义和声明是否匹配。
3、了解大小端模式吗?如何证明机器的大小端模式?…
内存地址是从小到大顺序增长的,说白了就是对于跨越多个字节的对象一般它所占的字节都是连续的,它的地址等于它所占字节的最低地址(链表可能是例外 但链表的地址可以看做表头的地址)也就是说一段连续的字节的头和尾哪个作为最低地址就是大端和小端问题。
因此,如果定义一个变量,变量的地址就是0x4000。采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。
下面通过程序判断大小端


4、谈谈C++的面向对象思想?谈谈C++的优点?…
把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一个事物在整个解决问题的步骤中的行为。
作者:温柔善良小小苏
链接:https://www.zhihu.com/question/460800309/answer/1906791443
1、保持与C兼容C++既保留了C语言的所有优点,又克服了C语言的缺点,其编译系统能检查出更多的语法错误,因此C++比C语言更安全。而且绝大多数C语言程序可以不经修改直接在C++环境中运行,用C语言编写的众多库函数可以用于C++程序中。C++设计成与C兼容,籍此提供一个从C到C++的平滑过渡。
2、支持面向对象的机制C++引入了面向对象的概念,使得开发人机交互类型的应用程序更为简单、快捷。很多优秀的程序框架Boost、QT、MFC、OWL、wxWidgets、WTL等都是使用C++开发出来的。
3、可重用性、可扩充性、可靠性和可维护性C++程序设计无需复杂的环境,它的很多特性都是以库(如STL)或其他形式提供,而没有直接添加到语言本身里,在可重用性、可扩充性、可维护性和可靠性等方面都较C语言有所提高,使其更适合开发大中型的系统软件和应用程序。
4、代码性能高人们一般认为,使用Java或C#的开发成本比C++低,但是,这句话成立是有一定条件的:软件规模和复杂度较小。如果不超过3万行的有效代码(不包括生成器产生的代码),它基本上成立,但随着代码量和复杂度的增加,C++优势将会越来越明显。
5、多种设计风格C++设计支持多种程序设计风格(过程化程序设计、资料抽象化、面向对象程序设计、泛型程序设计),给程序员更多的选择。尽管C++有很多优点,但它也像其他语言一样避免不了有缺点,C++语言本身过度复杂,导入模板后各种精巧的应用使这门语言进一步复杂化。并且C++编译器受到C++复杂性的影响,非常难于编写,即使能够使用的编译器也存在大量问题,而且这些问题大多难于发现。但是事物优缺点的同时存在是客观事实,我们应该正视这点。C++语言能够在大型项目中编写出高效率、高质量代码,但也要认识到这并不是一件易事,要深入掌握它需要花费较多时间,尤其是需要有较为丰富的实践经验。 C++是目前编程语言中最难的,初学者在学习C++时,面对复杂的C++语法与内容往往会心生退却。
5、C++函数内部如何调用并改变全局列表变量?…
6、介绍一下C++的继承?…
(1)什么是继承?…
继承(Inheritance)可以理解成为一个类从另一个类中获取成员变量和成员函数的过程。
(2)继承方式…
继承方式限定了基类成员在派生类中的访问权限,包括public(公有的)、private私有的和protected(受保护的)。此项是可选项默认是private。
public、protected、private 这三个关键字既可以修饰类的成员,还可以指定继承方式。
public、protected、private 修饰类的成员
类成员的访问权限由高到低依次为 public --> protected --> private,public 成员可以通过对象来访问,private 成员不能通过对象访问。protected 成员和 private 成员类似,也不能通过对象访问。但是当存在继承关系时,protected 和 private 就不一样了,基类中的 protected 成员可以在派生类中使用,而基类中的 private 成员不能在派生类中使用。
2.2 public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限
public继承方式
基类中所有 public 成员在派生类中为 public 属性;
基类中所有 protected 成员在派生类中为 protected 属性;
基类中所有 private 成员在派生类中不能使用。
protected继承方式
基类中的所有 public 成员在派生类中为 protected 属性;
基类中的所有 protected 成员在派生类中为 protected 属性;
基类中的所有 private 成员在派生类中不能使用。
private继承方式
基类中的所有 public 成员在派生类中均为 private 属性;
基类中的所有 protected 成员在派生类中均为 private 属性;
基类中的所有 private 成员在派生类中不能使用。
————————————————
原文链接:https://blog.csdn.net/weixin_44244332/article/details/123524742
(3)继承中的构造和析构…
在c++的类继承中,不管以什么样的方式继承,建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,以此类推;而析构对象时,其顺序正好与构造相反。
例子
#include
#include
using namespace std;
using namespace cv;
/*继承中的构造和析构顺序*/
class Fruit
{
public:
Fruit()
{
cout << "Fruit默认构造函数调用" << endl;
}
~Fruit()
{
cout << "Fruit析构" << endl;
}
};
class Apple : public Fruit
{
public:
Apple()
{
cout << "Apple默认构造函数调用" << endl;
}
~Apple()
{
cout << "Apple析构" << endl;
}
};
void test(){
Apple apple;
}
int main()
{
test();//Fruit构造->Apple构造->Apple析构->Fruit析构
waitKey(1000);
return 0;
}

子类是不会继承父类的构造函数和析构函数,父类没有默认构造函数,子类也不能有。

当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数。
#include
#include
using namespace std;
using namespace cv;
/*继承中的构造和析构顺序*/
class Fruit
{
public:
Fruit(int a)
{
this->m_A = a;
cout << "我是Fruit的有参构造" << endl;
}
int m_A;
};
class Apple : public Fruit
{
public:
//Apple() {};//因为基类中没有默认的构造函数 所以会报错
Apple(int a) :Fruit(a)//利用初始化列表的方式 显示调用 传值给基类有参构造
{
}
};
void test(){
Apple apple(5);
cout << apple.m_A << endl;
}
int main()
{
test();
waitKey(1000);
return 0;
}

(4)继承中的同名处理…
如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,那么就会遮蔽从基类继承过来的成员。所谓遮蔽就是在派生类中使用该成员(包括在定义派生类时使用,也包括通过派生类对象访问该成员)时,实际上使用的是派生类新增的成员,而不是从基类继承来的。
#include
using namespace std;
//基类
class Fruit
{
public:
Fruit(int fruitid);
void getFruitId();
protected:
int fruit;
private:
};
void Fruit::getFruitId()
{
cout << "我是水果第" << fruit << "号" << endl;
};
Fruit::Fruit(int fruitid)
{
this->fruit = fruitid;
}
//派生类
class Apple : public Fruit {
public:
void getFruitId();//遮蔽基类的getFruitId
Apple(int a) :Fruit(a) {};
};
void Apple::getFruitId() {
cout << "我是苹果第" << fruit << "号" << endl;
}
int main()
{
Apple apple(2);
//使用的是派生类新增的成员函数,而不是从基类继承的
apple.getFruitId();
//使用的是从基类继承来的成员函数
apple.Fruit::getFruitId();
}

基类的成员函数和派生类成员函数不构成重载
基类的成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数时要引起注意,不管函数的参数如何,只要名字一样造成就会造成遮蔽。换句话来讲,基类成员函数和派生类成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有的同名函数,不管它们的参数是否一样。
————————————————
(5)菱形继承…

虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余并且浪费空间的问题。
虚继承看起来结构较为复杂,一般不得已不要定义菱形结构的虚继承体系结构,因为使用虚继承虽然解决数据冗余问题但同时也带来了性能上的损耗,得不偿失。
7、堆和栈的区别?…
(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈大小要远远小于堆大小。理论上,进程可申请的堆大小为虚拟内存大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有 2 种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca()函数分配,但是栈的动态分配和堆是不同的,它的动态分配是由操作系统进行释放,无需我们手工实现。
(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。
(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者BSS段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。
————————————————
原文链接:https://blog.csdn.net/weixin_52244492/article/details/124426576
(1)栈内存…
(2)堆内存…
8、C++和python的区别?…
C++执行效率高,编程难(开发效率低);python执行效率低,编程简单(开发效率快;
C++ 为编译性编程语言,Python 则为解释性编程语言(Java,C#等都是解释型语言)。
编译型语言在程序执行之前,有一个单独的编译过程,将程序翻译成机器语言,以后执行这个程序的时候,就无需再进行编译,直接允许可执行文件即可。
解释型语言是指使用专门的解释器对源程序进行逐行解释成特定平台的机器码并立即执行的语言。解释型语言通常不会进行整体的编译和链接处理,解释型语言相当于把编译型语言中的编译和解释过程混合在一起同时完成。
每次执行解释型语言的程序都需要进行一次编译,因此解释型语言的程序运行效率通常较低,而且它不能脱离解释器独立运行。
但解释性语言有一个优势:跨平台比较容易,只需提供特定平台的解释器即可。每个特定平台上的解释器负责将源程序解释成特定平台的机器指令即可。解释性语言可以方便的实现源程序的移植,但这是以牺牲程序执行效率为代价的。
Python 与 C ++ 重要的区别之一是内存管理。Python 提供了被称为「垃圾收集器」的自动内存管理机制,不允许直接进行内存处理操作。但在 C++ 里则没有这样的机制,并且所有内存管理操作都需要自行处理。
————————————————
原文链接:https://blog.csdn.net/weixin_44763789/article/details/118058341
9、介绍一下多线程?…
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理“。
————————————————
原文链接:https://blog.csdn.net/wang121213145/article/details/123828346
(1)线程产生的原因?…
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;进程是系统资源分配的单位,线程是系统调度的单位。
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
进程之间相互独立,进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
调度和切换:线程上下文切换比进程上下文切换要快得多。
————————————————
原文链接:https://blog.csdn.net/wang121213145/article/details/123828346
(2)线程的优势?…
创建一个新线程的代价要比创建一个新进程小的多
线程之间的切换相较于进程之间的切换需要操作系统做的工作很少
线程占用的资源要比进程少很多
能充分利用多处理器的可并行数量
等待慢速 IO操作结束以后,程序可以执行其他的计算任务
计算(CPU)密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
IO密集型应用,为了提高性能,将IO操作重叠,线程可以等待不同的IO操作。
(3)多线程的优势?…
优点:
(1) 用户界面可以在进行其它工作的同时CPU一直处于活动状态,可以让程序运行速度更快。
(2)占用大量处理时间的任务可以定期将处理器时间让给其它任务,可以提高CPU利用率。
10、介绍一下C++的内存管理?…

①.内核空间: 操作系统内核代码的运行空间.
②.栈: 又叫做堆栈,非静态局部变量/函数形参/返回值/表达式中间结果/某些寄存器信息等等,栈是向下增长的.
③.内存映射段: 是高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享内存,进行进程间通信.
④.堆: 用于程序运行时动态内存分配,堆是向上增长的.
⑤.数据段: 存储全局数据和静态数据.
⑥.代码段: 可执行的代码/只读常量.
————————————————
原文链接:https://blog.csdn.net/weixin_49312527/article/details/123807423
11可以介绍下C++的智能指针吗?…
从比较简单的层面来看,智能指针是RAII(Resource Acquisition Is Initialization,资源获取即初始化)机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以方便管理一个对象的生命周期。
在c++中,智能指针一共定义了4种:
auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。其中,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用。
————————————————
原文链接:https://blog.csdn.net/bitcarmanlee/article/details/124847634
12、介绍下数组和链表的操作区别?…
首先从逻辑结构上说,两者都是数据结构的一种,但存在区别,
数组是申请的一块连续的内存空间,并且是在编译阶段就要确定空间大小的,同时在运行阶段是不允许改变的,所以它不能够随着需要的改变而增加或减少空间大小,所以当数据量大的时候,有可能超出了已申请好的数组上限,产生数据越界,或者是数据量很小,对于没有使用的数组空间,造成内存浪费。
链表则是动态申请的内存空间,并不像数组一样需要事先申请好大小,链表是现用现申请就OK,根据需求动态的申请或删除内存空间,对于的是增加或删除数据,所以比数组要灵活。
再从物理存储即内存分配上分析,
数组是连续的内存,对于访问数据,可以通过下标直接读取,时间复杂度为O(1),而添加删除数据就比较麻烦,需要移动操作数所在位置后的所有数据,时间复杂度为O(N)。
链表是物理上非连续的内存空间,对于访问数据,需要从头便利整个链表直到找到要访问的数据,没有数组有效,但是在添加和删除数据方面,只需要知道操作位置的指针,很方便可以实现增删,教数组比较灵活有效率。
所以综合以上,对于快速访问数据,不经常有添加删除操作的时候选择数组实现,而对于经常添加删除数据,对于访问没有很高要求的时候选择链表。
————————————————
原文链接:https://blog.csdn.net/weixin_48264057/article/details/109585140
13、引用头文件时双引号和尖括号的区别?…
尖括号用于包含标准库的头文件,一般没有.h后缀,编译器会去系统配置的库环境变量和者用户配置的路径去搜索,而不会在项目的当前目录去查找。
双引号一般用于包含用户自己编写的头文件,通常含有.h后缀,编译器会先在项目的当前目录查找,找不到后才会去系统配置的库环境变量和用户配置的路径去搜索。
————————————————
原文链接:https://blog.csdn.net/stopbl/article/details/77389140
二、Python语言…
1、Python中函数内部如何调用并改变全局列表变量?…
2、Python中的多线程?…
https://blog.csdn.net/weixin_40481076/article/details/101594705/
3、在使用pytorch或者tensorflow过程中有没有什么踩坑的经历?…
4、了解pytorch中的计算图吗?…
Pytorch的计算图由节点和边组成,节点表示张量或者Function,边表示张量和Function之间的依赖关系。Pytorch中的计算图是动态图。这里的动态主要有两重含义:
第一层含义是:计算图的正向传播是立即执行的。无需等待完整的计算图创建完毕,每条语句都会在计算图中动态添加节点和边,并立即执行正向传播得到计算结果。
第二层含义是:计算图在反向传播后立即销毁。下次调用需要重新构建计算图。如果在程序中使用了backward方法执行了反向传播,或者利用torch.autograd.grad方法计算了梯度,那么创建的计算图会被立即销毁,释放存储空间,下次调用需要重新创建。
————————————————
原文链接:https://blog.csdn.net/u013010473/article/details/123384191
5、Numpy与Pandas的区别?…
Numpy和Pandas的区别
Numpy是数值计算的扩展包,能够高效处理N维数组,即处理高维数组或矩阵时会方便。Pandas是python的一个数据分析包,主要是做数据处理用的,以处理二维表格为主。但注意这不是说Numpy就处理不了二维数据,它也可以处理。
Numpy只能存储相同类型的array,Pandas能处理不同类型的数据,例如二维表格中不同列可以是不同类型的数据,一列为整数一列为字符串。
Numpy支持并行计算,所以TensorFlow2.0、PyTorch都能和numpy能无缝转换。Numpy底层使用C语言编写,效率远高于纯Python代码。
Pansdas是基于Numpy的一种工具,该工具是为了解决数据分析任务而创建的。Pandas提供了大量快速便捷地处理数据的函数和方法。
Python因为有了NumPy与Pandas而不同于Java、C#等程序语言,Python也因为NumPy与Pandas而又一次的焕发了光彩。
6、python中用过迭代器、生成器吗?什么时候使用?…
https://blog.csdn.net/weixin_42317885/article/details/112041915
7、介绍一下python中的数据结构?…
https://blog.csdn.net/weixin_43229729/article/details/122833187
.8、介绍一下模型的压缩工具?以手机端为例?…