c++ 常见基础知识以及面试常问知识点

这些都是本人平时积累的知识点,比较杂也比较基础。但是对于常见C++面试,应该会有很大帮助 (本人做qt等客户端开发)

常见面试题:https://segmentfault.com/a/1190000003745529?utm_source=tag-newest

常用库 excel(libxl)
///dll导出///
#ifdef MAKEDLL_EXPORTS
#define MAKEDLL_API __declspec(dllexport)
#else
#define MAKEDLL_API __declspec(dllimport)
#endif
为什么要使用__declspec(dllexport)与__declspec(dllimport),及两者的区别:都是DLL内的关键字,即导出与导入。他们是将DLL内部的类与函数以及数据导出与导入时使用的。主要区别在于,dllexport是在这些类、函数以 及数据的申明的时候使用。用过表明这些东西可以被外部函数使用,即(dllexport)是把DLL中的相关代码(类,函数,数据)暴露出来为其他应用程 序使用。使用了(dllexport)关键字,相当于声明了紧接在(dllexport)关键字后面的相关内容是可以为其他程序使用的。而 dllimport关键字是在外部程序需要使用DLL内相关内容时使用的关键字。当一个外部程序要使用DLL内部代码(类,函数,全局变量)时,只需要在 程序内部使用(dllimport)关键字声明需要使用的代码就可以了,即(dllimport)关键字是在外部程序需要使用DLL内部相关内容的时候才使用。(dllimport)作用是把DLL中的相关代码插入到应用程序中。如果没有使用dllexport导出函数,将无法生成lib文件,只能生成dll文件。


MFC 消息机制
https://blog.csdn.net/sunnzhongg/article/details/53646507

Windows程序的入口是哪里?写出Windows消息机制的流程。 
入口点是WinMain函数.
A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中 
B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。 
C. 应用程序调用DispatchMessage,将消息回传给操作系统。 
D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理

注意:消息映射为什么不是虚函数呢?
答:C++有一个名为vtable的虚函数分发表。如果用虚函数发送消息,CWnd将为超过100个消息来申明虚函数。对于每个虚函数,vtable中对应有4个字节,那么应用程序将需要400多个字节的表来支持虚拟消息处理函数。所以为了避免大型的vtable,MFC使用宏来把WINDOWS消息连接到C++成员函数。MFC消息处理程序需要函数原型,函数体和在消息映射中的输入项(宏调用),ClassWizard帮助我们将消息处理程序添加到类中。

Windows消息机制的流程:
1.Windows中有一个系统消息队列,对于每一个正在执行的Windows应用程序,系统为其建立一个“消息队列”,即应用程序队列,用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作“消息循环”的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。
2.Windows为当前执行的每个Windows程序维护一个「消息队列」。在发生输入事件之后,Windows将事件转换为一个「消息」并将消息放入程序的消息队列中。程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
while(GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
TranslateMessage(&msg);将msg结构传给Windows,进行一些键盘转换。
DispatchMessage (&msg);又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。
SendMessage()与PostMessage()之间的区别是什么?
它们两者是用于向应用程序发送消息的。PostMessagex()将消息直接加入到应用程序的消息队列中,不等程序返回就退出;而SendMessage()则刚好相反,应用程序处理完此消息后,它才返回。

引用与指针的区别
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名。对引用的操作就是对目标变量的操作。
对指针执行sizeof()操作得到的是指针本身的大小(32位系统4,64位系统8)。而对引用sizeof()操作。


final 一个类无法被别的类继承

特殊成员函数
缺省构造函数 析构函数 拷贝构造函数 赋值运算符 取值运算符 取值运算符const 
Mutable 可变的。为了突破const
2. const 有什么用途
    主要有三点:
      1:定义只读变量,即常量 
      2:修饰函数的参数和函数的返回值 
      3: 修饰函数的定义体,这里的函数为类的成员函数,被const修饰的成员函数代表不修改成员变量的值
 	在类中声明变量为const类型,但是不可以初始化,初始化必须在构造函数初始化列表中,不可以在构造函数体内初始化
注意点:
想禁止拷贝和赋值构造函数。可以把其设置为private
对虚函数实现多态情况。需要在基类中析构函数+virtual。不然会造成局部销毁的现象。形成资源泄露 (带多态性质的基类应该声明一个virtual析构函数,日过类带有任何virtual函数。它就应该拥有一个virtual析构函数. 类的涉及目的如果不是作为基类来使用。或者不是为了多态性,就不应该声明为virtual析构函数)
析构函数: (1)绝对不要再析构吐出异常。如果一个被析构函数调用的函数可能抛出异常。析构函数应该捕捉任何异常。然后吞下他们或者结束程序 (2)如果客户需要对某个操作函数运行期间抛出的异常做出反应,。那么class应该提出一个普通函数执行该操作
在构造和析构期间不要调用virtual函数。,因为这类调用从不下降至derived class
令赋值操作符返回一个reference to *this
Copying函数应该确保复制“对象内的所有成员变量”及所有base class 成员
在防止资源泄露,请使用RAII对象,他们在构造函数中获得资源并在析构函数中释放资源
两个常用使用的RAII(也称为“资源获取就是初始化”) classes分别是tr1::shared_ptr和auto_ptr 前者通常是较佳选择。因为其copy行为比较直观,若选择auto_ptr.,复制动作会使他指向null 【std::tr1::shared_ptr】
复制RAII对象必须一并复制它所管理的资源,所以资源的cpoying行为决定RAII对象的copying行为 【条款14】
普遍而常见的RAII class copying行为是:抑制copying 施行引用计数法
Delete object //删除一个对象 delete [] objectArray //删除一个数组
好的接口容易被正确使用,应该在所以接口中努力达成这些性质
“促进正确使用”办法包括接口的一致性以及与内置类型的行为兼容
“阻止误用”的办法包括建立新类型,限制类型上的操作,束缚对象值以及消除客户的资源管理责任
Tr1::shared_ptr支持定制型删除器 
尽量以pass-by-reference-to-const 替换pass-by-value 前者通常比较高效,并可以避免切割问题
以上规则并不适用于内置类型,以及stl的迭代器和函数对象,对他们而言,pass-by-value往往比较适当
绝对不要返回pointer或reference指向一个local stack 对象或返回reference指向一个heap-allocated 或返回pointer或reference对象。或返回pointer或reference执行一个local static对象而有可能同事需要多个这样的对象
为内置型对象进行手工初始化。因为C++不保证初始化他们
构造函数最好使用成员初始化列,而不是在构造函数本体内使用赋值操作,初值列累出的成员变量。其排列次序应该和他们在class中声明的次序相同
为免除“跨编译单元之初始化次序”问题,请以local static 对象替换non-localstatic 对象
切记讲成员变量声明为private。这可赋予客户访问数据的一致性,可细微划分访问控制,允诺约束条件获得保证,并提供class作者以充分的实现弹性
Protectd并不比public更具有封装性
宁可拿non-member non-friend 函数替换member函数,这样做可以增加封装性,包裹弹性和机能扩充性

Std::shared_ptr 使用
std::enable_shared_from_this
当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr
Std::lock_guard  //const std::lock_guard lock(participants_mutex_); 
Std::find_if
std::lock_guard 在构造函数中进行加锁,析构函数中进行解锁。
unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
unique_lock比lock_guard使用更加灵活,功能更加强大。try_lock:尝试加锁
/*
//表示如果bool= true的时候表示查找到然后就可以删除
participants_.erase(std::find_if(
      participants_.begin(), participants_.end(),
      [&](std::shared_ptr o) -> bool { return o->Id() == id; }));*/
Static_cast 和dynamic_cast 和const_cast 和reinterpret_cast
dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。支持父类指针到子类指针的转换,这种转换时最安全的转换。它 是唯一不能用旧风格语法执行的强制类型转换,也是唯一可能有重大运行时代价的强制转换。将指向基类basic class object的pointer或者reference转型为指向派生类derived(或这sibling base)class object的pointer或者reference中,并且可以获知是否转型成功:如果转型失败,当转型对象是指针的时候会返回一个null指针;当转型对象是reference会抛出一个异常exception。dynamic_cast无法应用在缺乏虚函数的类型上,也不能改变类型的常量性。

static_cast 可以被用于强制隐形转换(例如,non-const对象转换为const对象,int转型为double,等等),它还可以用于很多这样的转换的反向转换 (例如,void*指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个const对象转型为non-const对象(只有 const_cast能做到),它最接近于C-style的转换。应用到类的指针上,意思是说它允许子类类型的指针转换为父类类型的指针(这是一个有效的隐式转换),同时,也能够执行相反动作:转换父类为它的子类
const_cast一般用于强制消除对象的常量性。它是唯一能做到这一点的C++风格的强制转型。这个转换能剥离一个对象的const属性,也就是说允许你对常量进行修改。
reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制类型在底层代码以外应该极为罕见。操作 结果只是简单的从一个指针到别的指针的值得二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。

C++11 
std::< atomic> 原子操作
std::tie 创建一个元组的左值引用
std::tuple 元组

3. 指针和引用的区别
    1:引用是变量的一个别名,内部实现是只读指针
    2:引用只能在初始化时被赋值,其他时候值不能被改变,指针的值可以在任何时候被改变
    3:引用不能为NULL,指针可以为NULL
    4:引用变量内存单元保存的是被引用变量的地址
    5:“sizeof 引用" = 指向变量的大小 , "sizeof 指针"= 指针本身的大小
    6:引用可以取地址操作,返回的是被引用变量本身所在的内存单元地址
    7:引用使用在源代码级相当于普通的变量一样使用,做函数参数时,内部传递的实际是变量地址
 
4. C++中有了malloc / free , 为什么还需要 new / delete     
  1,malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
  2,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
     对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
     由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
  3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

5. 什么是虚函数?什么是纯虚函数?
答:虚函数声明如下: virtual ReturnType FunctionName(Parameter);引入虚函数是为了动态绑定。 
纯虚函数声明如下:virtual ReturnType FunctionName()= 0;引入纯虚函数是为了派生接口。

6 . 基类为什么需要虚析构函数?
答:标准规定:当derived class经由一个base class指针被删除而该base class的析构函数为non-virtual时,将发生未定义行为。通常将发生资源泄漏。 
解决方法即为:为多态基类声明一个virtual 析构函数
总的来说是为了避免内存泄露,而且是当之类中会有指针成员变量时才会使用得到的。也就是说虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放之类中堆内存的目的。而防止内存泄露

7. 堆和栈的区别   
  一个由c/C++编译的程序占用的内存分为以下几个部分 
  1、栈区(stack)―   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 
  2、堆区(heap) ―   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
     注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 
  3、全局区(静态区)(static)―,全局变量和静态变量的存储是放在一块的,
     初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
  4、文字常量区  ―常量字符串就是放在这里的。 程序结束后由系统释放 
  5、程序代码区―存放函数体的二进制代码。
8. 关键字static的作用
1.  函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
2.  在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
    3.  在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
    4.  在类的static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝
5.  在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量

static 全局变量与普通变量的区别是。static全局变量只初始化一次。防止在其他文件单元中被引用
static 局部变量和普通局部变量的区别是。static局部变量只初始化一次。下一次依据上一次结果值 
static函数与普通函数的区别是。static函数在内存中只有一份。普通函数在每个被调用中维持一份复制品

未经初始化的静态全局变量会被程序自动初始化为0(在函数体内声明的自动变量的值是随机的,除非它被显式初始化,而在函数体外被声明的自动变量也会被初始化为0);

9. 头文件种的ifndef/define/endif 是干什么用的
      防止头文件被重复包含
10. Mutable用法 
	Mutable 是为了突破const的限制而设置的。被mutable 修饰的变量。将永远处于可变的状态,计时在一个const函数中。
参考地址:https://blog.csdn.net/starlee/article/details/1430387
11. 线程的基本概念、线程的基本状态及状态之间的关系?
线程,有时称为轻量级进程,是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。
线程有四种状态:新生状态、可运行状态、被阻塞状态、死亡状态。状态之间的转换如下图所示:
12. c++怎样让返回对象的函数不调用拷贝构造函数
拷贝构造函数前加 “explicit” 关键字 去除隐式转换
C++ 11 delete 

13 STL 分析
Vector resize reserve 
reserve和resize都不会使capacity变小,但都有可能使capacity变大,具体怎么变大,reserve和resize是不一样的,reserve能准确控制capacity;而resize不能,vc里是每次增大一半的当前capacity
每当capacity不够时,就会去allocate一块新的够大的内存,再释放以前的内存,效率是很低的
清空内存 
使用方法举例:
 vector(v).swap(v);将v的内存空洞清除
 vector().swap(v);清空vec
reserve() 是容器预留空间。但并不是真正创建元素,在创建之前。不能引用容器内元素,因此当新加入元素。需要用到push_back/inser()函数
resize() 是改变容器大小,并创建对象。因此。调用这个函数之后。就可以引用容器内的对象了。因此当加入新元素时

STL的vector :增减元素对迭代器的影响
主要问题是针对连续内存容器还是非内存容器 
a..对于连续内存容器 ,如:vector deque 增减元素均会使得当前之后的所有迭代器失效。因此。以删除为例。由于erase()总指向被删除元素的下一个元素的迭代器。因此。可以利用连续内存容器成员erase()函数的返回值
for(auto iter = myvec.begin(); iter != myvec.end())  //另外注意这里用 "!=" 而非 "<"
    {
        if(delete iter)
            iter = myvec.erase(iter);
        else ++iter;
    }

b .对于非连续内存容器,如set map .增减元素只会对当前迭代器无效。由于删除后。erase()、返回的迭代器将是无效的迭代器。因此在调用erase()之前,使得迭代器指向删除元素的下一个元素
 for(auto iter = myset.begin(); iter != myset.end())  //另外注意这里用 "!=" 而非 "<"
    {
        if(delete iter)
            myset.erase(iter++);  //使用一个后置自增就OK了
        else ++iter;
    }

14 win32创建窗口
定义窗口类
注册窗口 RegisterClass
创建窗口 CreateWindow
显示窗口 ShowWindows
消息处理
消息循环
每一个输入时间生成一个消息。根据时间发生时的情况,操作系统会确定出此消息应该发给哪个窗口对象。这些生成消息会统一地先临时放置在一个“系统消息队列”中。然后,操作系统有一个专门的线程负责从这一队列中取出消息,根据消息的目标对象(就是窗口的句柄),将其移动到创建它的UI线程所对应的消息队列中。操作系统在创建进程和线程时。都同时记录了大量的控制信息。
注意:每一个UI线程都有一个消息队列。而不是每个窗体一个消息队列
操作系统是不是会为每一个线程都创建一个消息队列呢?
答案: 只有当一个线程调用Win32 API中的GDI和user函数时。操作系统才会将其看成是一个UI线程。并为他创建一个消息队列。需要注意的是。消息循环是由UI线程的线程函数启动的。操作系统不管这件事,它只管UI线程创建消息队列。因此,日过某个UI线程的线程函数没有定义消息循环。那么,他所拥有的窗体是无法正确绘制的。


一:MFC消息传递机制:
#define WM_MyMessage (WM_USER+100)
头文件中添加 afx_msg LRESULT OnMyssage(WPARAM LPARAM);
在消息映射中添加 ON_MESSAGE(WM_MyMessage,OnMyMessage)
实现自定义消息处理 LRESULT OnMyMessage(WPARAM wp,LPARAM lp)
Sendmessage 或 postmessage
如果要定义系统唯一的消息让多个应用程序去处理,不同之处如下:
(1). 把1步骤宏 替换成static UNIT WM_MyMessage = RegisterWindowMessage(“”);
(2). 在消息映射里面修改 ON_REGISTERED_MESSAGE(WM_MyMessage,OnMyMessage)
	   (3). 测试消息时,如果要让多个应用程序都接收到这个消息,使用::sendMessage(HWND_BROADCAST,WM_MyMessage,0,0);
二:模式对话框和非模式对话框的区别 (消息循环)
在MFC框架中,一个对话框对象DoModal能产生一个模式对话框,create产生非模式对话框,实际上,无论模式对话框还是非模式,在MFC内部都是调用createdialogindirect()函数来创建对话框,只是模式对话框做了更多的工作,包括使父窗口无效,然后进入自己的消息循环等等,
::createDialogIndirect函数最终调用CreateWindowEx函数通知系统创建窗体并返回句柄,内部是没有实现自己的消息循环。非模式对话框创建之后立即返回并且和主程序公用一个消息循环。模式对话框等结束才返回,自己有消息循环                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
参考地址:https://blog.csdn.net/wangjieest/article/details/7239475


15:线程与进程的区别?

1、 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。
2、 一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个进程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。
3、 系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。那就是说,出了CPU之外(线程在运行的时候要占用CPU资源),计算机内部的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。
4、 与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。
5、 进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。

16:多线程有几种实现方法,都是什么?
 Window 三种实现多线程同步方法
临界区被初始化后,当程序进入临界区后便有了临界区的所有权,其余线程无权进入只能等待对方释放临界区之后,方可进入临界区
InitializeCriticalSection() 初始化临界区
EnterCriticalSection() 进去临界区
LeaveCriticalSection() 释放临界区所有权并离开临界区
注意:上述是windows API中相关函数,CCriticalSection类是MFC中定义的临界区类,需要在MFC程序中使用(此程序为控制台程序无法使用MFC类),可以操作临界区,lock锁定临界区、unlock释放临界区
	  临界区为依次访问,不能实现其中一个线程一释放临界区就会被另一个线程访问临界区!不能实现实时监听
事件对象
事件对象是指用户在程序中使用内核对象的有无信号状态实现线程的同步临界区被初始化后,当线程进入临界区后便拥有临界区。其余线程无权进入只能等对方释放临界区之后,方可进入临界区拥有其所有权再对临界区进行操作
CreateEvent()  创建并返回事件对象
SetEvent() 讲指定的时间设置为有信号状态(有信号状态下其余线程可以访问)
ResetEvent() 将指定的时间设置为无信号状态
     除了SetEvent函数外。WaitForSingleObject函数等待指定事件
	注意:上述是Windows API函数,CEvent类是MFC实现事件对象的类  事件对象为立即访问,一旦事件对象被设	置为有信号 立刻会被其余线程访问!能实现实时监听
使用互斥对象
互斥对象还可以在进程间使用,在实现线程同步时包含一个线程ID和一个计数器,线程ID表示用户胡策对象的线程,计数器表示该互斥对象被同一线程所使用次数
CreateMutex() 创建并返回互斥对象
ReleaseMutex() 释放互斥对象句柄
WaitForSingleObject() 对该对象进行请求
注意:上述是Windows API函数,CMutex类是MFC中的互斥对象类,,互斥对象为立即访问,一旦互斥对象被释放 立刻会被其它正在等待的线程访问!能实现实时监听
 
17:多线程同步和互斥有几种实现方法,都是什么?
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量
临界区与互斥量的区别
临界区: 用来保证在同一时刻只有一个线程可以访问到资源,对于临界区进行操作。临界区最大的特色是其同步速度很快,但是其只能用来同步本进程内的线程,而不可用来同步多个进程中的线程
互斥量:只有拥有互斥对象的线程才具有访问资源的权限,互斥量比临界区负责,并且互斥量是可以命名的。因此互斥量不仅仅可以用于同一应用程序不同线程中的资源的同步,也可以用于不同应用程序的线程之间对资源的同步

临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操作,速度慢。
临界区域互斥的区别:在Win32中。临界区由一个或多个不同的代码段组成,在任一时刻只能执行其中的一个。在win32中,临界区与互斥的区别是临界区只限于单个进程,而互斥锁能够越过进程的边界并使在不同进程中运行的线程同步。
18. 多线程中堆与栈(stack)是公有还是私有的
	栈私有 堆公有
19.临界区(Critical Section) 和互斥量(Mutex)
	(1) 两者都可以用于在同一进程中不同子线程对资源的互斥访问
	(2) 互斥量是内核对象。因此还可以用于不同进程中子线程对资源的互斥访问
	(3) 互斥量可以很好的解决线程意外终止资源无法释放的问题
20. 信号量
	CreateSemaphore (安全控制,初始资源数量(一般传入null),最大并发数量,信号量的名称(传入NULL表示匿名信号量))创建信号量
	openSemaphore 打开信号量
	ReleaseSemaphore 增加信号量
	由于信号量是内核对象。因此使用closeHandle() 就可以完成清理与销毁
参考地址:https://blog.csdn.net/handsomeslow/article/details/47039999

21:多线程同步和互斥有何异同,在什么情况下分别使用他们?举例说明。
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

22. 进程间的通信方式
     管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件.
23. 线程同步的方式
     Linux:   互斥锁、条件变量和信号量
24. TCP和UDP有什么区别
     TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。
                 当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
                 TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
     UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。
                 UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
                 由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
25、HTTP连接
HTTP协议是建立在TCP协议之上的一种应用。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”
26、SOCKET原理
五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

Http post 与get 的区别
1. get在浏览器回退时是无害的,而post会再次提交请求;
2. get产生的url可以被添加到书签,而post不可以;
3. get请求会被浏览器主动缓存,而post不会,除非手动设置;
4. get请求只能进行url编码,而post支持多种编码方式;
5. get请求参数会被完整保留在浏览器历史记录里,而post中的参数不会被保留;
6. get请求在url中传送的参数是有长度限制的,而post没有;
7. 对参数类型,get只接受ASCII字符,而post没有限制;
8. get会让参数直接暴露在url上,所以敏感信息建议post;
9. get参数通过url传递,post放在request body中;
关于第2条:你登陆一个网站,然后复制了其中的一个url,发送给你的朋友,但你的朋友点开却是和你看到不是同一个页面,而是你登陆之前的页面的信息。
关于第6条:说get请求有长度限制,是因为get通过url提交数据,那么其长度就直接和url的长度有关了。而实际上,url不存在参数上限的问题,HTTP规范中并没有对url长度进行限制,这个限制是浏览器及服务器对它的限制。
关于第8条:其实post发送敏感信息不像get直接在url中被我们看到,但我们可以用抓包的方式依然可以看到这些敏感信息,只不过post看不到是因为浏览器做了处理,如果post是绝对安全的话,我们就不会用到https了。
总结:
get的主要任务是获取数据,但也可以在获取数据之前向服务器提交一些数据;
post是主要任务是提交数据,但在提交数据后服务器也可以向用户返回一些数据;
get请求的参数数据在request-line中(请求行);post请求的参数数据既可以在request-line中,也可以在request-body(请求主体)中。


27、SOCKET连接与TCP连接
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接
28 mysql存储引擎
InnoDB:支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。 
MyISAM:插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可以使用。
MEMORY:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。

Insert into 和 replace into 区别
replace into 跟 insert 功能类似,不同点在于:replace into 首先尝试插入数据到表中, 1. 如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据。 2. 否则,直接插入新数据。
要注意的是:插入数据的表必须有主键或者是唯一索引!否则的话,replace into 会直接插入数据,这将导致表中出现重复的数据。


Mysql 索引类型  
	普通索引 唯一索引 主键索引 组合索引 全文索引
	注意事项:
索引不会包含有null值的列
使用短索引
索引列排序
like语句操作
不要在列上进行运算
不使用not in和<>操作
29. 使用过的 gdb 命令
      http://blog.csdn.net/dadalan/article/details/3758025
30 sizeof() 和strlen() 函数 
	Sizeof()是C/C++中的一个操作符,其作用是返回一个对象或者类型所占用的内存字节数。Sizeof()操作符返回值类型size_t 该类型可以保证能够容纳实现所建立的最大对象的直接大小。
	Strlen() 用于计算不包含终止符’\0’在内的字符串长度。而sizeof()则计算包括终止符’\0’在内的缓冲区长度
31. 基于套接字API的C/S网络模型

UDP服务器端算法的步骤描述:
调用socket()函数创建服务器端无连接套接字
调用bind() 函数将套接字绑定到本地的一个可用的端点地址
调用recvfrom()函数从套接字接收来自远程客户端的数据并存入到缓冲区,同时获得远程客户的套接字端点地址并保存
基于保存的远程客户的套接字端点地址,调用sendto() 函数将缓冲区中的数据从套接字发送给该远程客户
与客户交互完毕。调用close() 函数将套接字关闭。释放所占用的系统资源。
UDP客户端算法步骤描述
调用socket()函数创建客户端无连接套接字
找到期望与之通信的远程服务器的IP地址和协议端口号。然后调用sendto()函数将缓冲区中的数据从套接字发送给远程服务器
调用recvfrom()函数从套接字手来远程服务器端的数据并存入缓冲区
与服务器交互完毕,调用close() 函数将套接字关闭,释放所占用的系统资源

32. 获得一个进程的父进程的进程标识
Getppid() 函数的返回值即为当前进程的父进程的进程号
Getpid(void) 返回当前进程的进程号

僵尸进程的清楚
方法1: 父进程可以通过调用wait() 或waitpid() 等函数来等待子进程结束。从而避免产生僵尸进程,但这会导致父进程被挂起(即父进程被阻塞,出于等待状态)
方法2: 如果父进程很忙而不能被挂起,那么可以通过调用signal() 函数为SIGCHLD 信号安装handler 来避免产生僵尸进程。因此当子进程结束后。内核将会发送SIGCHLD信号给其父进程,而父进程在收到该信号之后,则可以在handler中调用wait()函数来进程回收。
方法3: 如果父进程不关心子进程何时结束,那么可以通过调用signal(SIGCHLD,SIG_IGN)函数来通知内核,表明自己对子进程的结束不感兴趣,那么子进程结束后将会被内核自动回收,则不会再给父进程发送SIGCHLD信号,因此可以避免产生僵尸进程
方法4: 由于当一个父进程死后,其子进程将成为“孤儿进程”,从而会被过继给 l 号进程 init , init 是系统中的 · 个特殊进程,其进程 ID 为 I ,主要负责在系统启动时启动各种系统服务及子进程的清理,只要有子进程终止. init 就会调用 wait 函数清理它。因此,当一个父进程死后,其产生的僵尸进程也会被过继给 1 号进程 init ,再由 init 进程负责自动清理,这样一来,就使得一个父进程也可通过 fork 两次来避免产生僵尸进程,具体实现步骤如下:首先,父进程 fork 一个子进程并继续工作,然后该子进程再在 fork 了一个孙进程之后退出,由于该孙进程将会被 init 进程接管,因此当该孙进程结束之后,将会被 init 进程自动回收。当然,子进程的回收工作还得由父进程来负责。
33设计模式
单例模式 工厂模式 观察者模式

34 智能指针
share_ptr 和unique_ptr

35 在一个长度为n的带头结点的单链表h上,另设有尾指针r(指向尾结点),在单链表最后一个元素之后插入一个新元素p,执行的操作(   r->next=p;p->next=NULL;  r=p;   )

36当我们在局域网内使用 ping www.jd.com 时,那种协议没有被使用?( TCP   )
1、因为ping的话 后面跟的是地址,所以要先将域名转换为ip地址,即用到了DNS
2、获取到ip地址后,在数据链路层是根据MAC地址传输的,所以要用到ARP解析服务,获取到MAC地址
3、ping功能是测试另一台主机是否可达,程序发送一份ICMP回显请求给目标主机,并等待返回ICMP回显应答,(ICMP主要是用于ip主机、路由器之间传递控制信息,控制信息是指网络通不通,主机是否科大)
4、TCP的话,不涉及数据传输,不会用到

37 判断应用程序是否有相同实例存在
	CString StrName="InstanceExist";
	hMutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,StrName);
	if(hMutex==NULL)
	{
		hMutex=::CreateMutex(NULL,NULL,StrName);
	}
	else
	{
		if(AfxMessageBox("实例程序已经存在,是否退出?",MB_ICONQUESTION|MB_YESNO)==IDYES)
			return FALSE;
	}
dwDesiredAccess:
MUTEX_ALL_ACCESS 请求对互斥体的完全访问
MUTEX_MODIFY_STATE 允许使用 ReleaseMutex 函数
SYNCHRONIZE 允许互斥体对象同步使用
bInheritHandle : 如希望子进程能够继承句柄,则为TRUE

38 字符串比较 
	CompareNoCase与Compare
31 状态栏操作
	隐藏显示状态栏ShowWindow(FindWindow(“Shell_TrayWnd”,NULL),SW_HIDE);
	获取状态栏启动的窗口 CWND* pWnd = AfxGetMainWnd()->GetWindow(GW_HWNDFIRST)
32 删除快捷键以及清空回收站
	删除快捷键
	清空回收站:SHEmptyRecycleBin
	开机自启:修改注册表Software\\Microsoft\\Windows\\CurrentVersion\\Run
33 按键 鼠标等消息PreTranslateMessage
34.串口编程 项目中
35.wireshark 简单命令  
36 libcef 基本原理



37 QT
(1). Qimage Qpixmap Qpictrue
 Qimage类提供了与硬件无关的图像表示,他为直接操作像素提供优化,Qimage 支持单色 8-bit 32bit 和alpha 混合图像,使用Qimage的有点是可以获得与平台无关的绘制操作,另外还有一个好处是图像可以不必在GUI线程中处理 
Qpixmap 是后台显示的图像,他为在屏幕上显示图像提供优化,不同于Qimage pixmap的图像数据是用户不可见而且由底层窗口系统管理,为了优化Qpixmap绘图,qt提供QpixmapCache类来存储临时的pixmap;
qPictrue 是能够记录和重演Qpainter命令的绘图设备,pictrue串行化painter的命令为平台无关的格式,Qpictrue同时也是与分辨率无关,如Qpicture能够在不同设备上(如svg pdf ps 打印机和屏幕)有一致的显示,Qpicture::load()和save()分别完成载入和存储图像;
(2) QdataStream 和QtextStream
(3) 临时文件
(4)文件传输QFTP 异步方式
(5)超文本传输协议QHTTP异步方式
(6)UDP应用 (数据报 )
	使用4种情况: 1 .网络数据大多为短信息
				2.拥有大量客户端
				3.对数据安全性无特殊要求
				4. 网络负担非常重,但对相应速度要求高
	广播: QUdpSocket writeDatagram  
	多播 (Qt5 12章网络)

(7)QTCP (精通Qt4编程P248) 继承自QAbstractSocket 和QIODevice 可以使用QTextStream和QDataStream
	Tcp 例子:上传文件到server .连接时第一次发送文件总长度 文件名长度 文件名 告诉server 文件信息
	异步
QHostInfo 类 主机名解析
QNetworkProxy 代理

QNetworkAccessManager,QNetworkRequest QNetworkReply
QNetworkAccessManager用于管理网络请求和响应,可以在网络上管理请求和响应的设置,使用代理和缓存、处理网络请求和响应过程中触发的信号
(8) Qt 互斥和同步类: 精通qt4 第10章
	设置线程优先级:Priority priority()const 和setPriority(Priority pro) 用于获取和设置当前正在运行的线程的优先级。另一种设置线程优先级在启动线程start(Priority priority = InheritPriority)
		QMutex 
		QMutexLocker,简化互斥量的处理:QMutexLocker locker(&mutex);
		QReadWriteLocker QReadLocker QWriteLocker 
		   则在条件满足时唤醒所以等待线程
线程等待条件 QWaitCondition 
wake() wakeAll() 唤醒线程
wait() 使用时必须传入一个上锁的QMutex https://blog.csdn.net/flyoxs/article/details/54617342


线程本地存储(thread-localstorage TLS) 线程特定存储(thread-specific data TSD)
(9) 处理死锁 1.死锁预防 2 死锁避免 3 死锁监测 4 死锁解除  (优先级反转)

GUI线程与子线程交互: P280 网络请求
Qt 事件处理方法 5种方式
重新实现特定的事件处理器
重新实现QObject::event()函数
在QObject中注册事件过滤器   通过
在QApplication 中注册事件过滤器
继承QApplication并重新实现notify()函数

ProcessEvents() aApp->processEvent() 函数调用替换为aApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); 
函数调用。告诉Qt忽略鼠标和键盘事件

使用定时器 特定定时器 “0毫秒定时器” P295
(10) 数据库操作
QSqlDatabase
模型/视图结构 15章
(11) 常用网络请求返回码
(12)   QtScript

Bool qFuzzyCompare(double p1,double p2) 比较浮点值的函数
Bool qFuzzyCompare(float p1,float p2) 
QtMsgHandler qInstallMsgHandler(QtMsgHander handler); 用户自定义函数。输出qt定义的调试
Qint64 qRound64(qreal value) 64位整型四舍五入函数
Int qRound(qreal value) 整型四舍五入函数
QByteArray 进行base64编解码


QHash QMultiHash
QMap QMultiMap contains()
QList 链表形式存储。快速索引访问,快速删除数据
注意:QList 与stl中list的区别
QVertor QStack QQueue 
QSet
QByteArrayMatcher 在直接数组中查找匹配的字节数组模式


QThread 多种使用 问题解析 https://www.cnblogs.com/liushui-sky/p/5829563.html
XML解析 Qt5 11章
DOM与SAX模式 
	SAX模式并不将解析自XML文档的所有对象上传到内存 所以其比DOM模式占用的内存少。SAX模式适用于只读文件的一部分并判断其有效性,或者需要解析的文档比较多且比较大的情况
	DOM 主要用于对XML文档进行结构性访问,

进程间通讯

共享内存QSharedMemory类。可以通过多线程和进程访问共享内存区域。QSystemSemaphore类 用于访问系统共享资源以实现独立进程间的通信。换言之。QSharedMemory用于访问应用程序和进程间的共享内存区域,而QSystemSemaphore类主要用于访问独立进程间的系统共享资源
D-bus 适用于linux和unix

条件变量和信号量的区别
条件变量有广播的功能。所以当实现订阅的功能。需要广播时间段时候必须使用条件变量。而信号量只能触发一个订阅

音视频
	QMediaplayer QVideoWidget QGraphicsVideoItem
	QVideoFrame 视频帧处理 QMediaRecorder录制视频数据 QVideoProbe类回放功能


Qt数据库 qt5 14章



虚函数和纯虚函数区别
1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。 
2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。
3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。
4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
5. 虚函数的定义形式:virtual {method body}
  纯虚函数的定义形式:virtual { } = 0;
在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。
6. 虚函数必须实现,如果不实现,编译器将报错,错误提示为:
error LNK****: unresolved external symbol "public: virtual void __thiscall
ClassName::virtualFunctionName(void)"
7. 对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。
8. 实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖
该虚函数,由多态方式调用的时候动态绑定。
9. 虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的
函数
10. 多态性指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
a.编译时多态性:通过重载函数实现
b 运行时多态性:通过虚函数实现。
11. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法


Git
git pull 相当于从远程获取最新版本并merge到本地
git merge 用来做分支合并,将其他分支中的内容合并到当前分支中

#ifndef   UNUSED_PARAM 
#define   UNUSED_PARAM(v)   (void)(v) 
#endif 
 
假如一个有返回值的函数 
如调用时是没有使用它的返回值,编译器会给出一个警告 
如果用void强制转换一下,则明确告诉编译器不使用返回值 
也就是为了消除警告

12. 右值引用 避免深拷贝
左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值
右值引用,是对临时对象的一种引用,它是在初始化时完成引用的,但是右值引用不代表引用临时对象后就不能改变右值引用所引用对象的值,仍然可以在初始化后改变临时对象的值。
对于引用类型,可以用于它所引用对象类型的可以用的地方(把它当成普通变量),只不过用到的值是它所引用的对象的值,它还可以用于移动构造或赋值函数的地方。
int &a = 2;       # 左值引用绑定到右值,编译失败
int b = 2;        # 非常量左值
const int &c = b; # 常量左值引用绑定到非常量左值,编译通过
const int d = 2;  # 常量左值
const int &e = c; # 常量左值引用绑定到常量左值,编译通过
const int &b =2;  # 常量左值引用绑定到右值,编程通过
拷贝构造函数细节:
为什么拷贝构造函数必须是引用传递,不能是指传递
防止递归引用。
当 一个对象需要以值方式传递时,编译器会生成代码调用它的拷贝构造函数以生成一个复本。如果类A的拷贝构造函数是以值方式传递一个类A对象作为参数的话,当 需要调用类A的拷贝构造函数时,需要以值方式传进一个A的对象作为实参; 而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导 致又一次调用类A的拷贝构造函数,这就是一个无限递归
	参考地址:https://www.cnblogs.com/wuchanming/p/4050969.html
虚拟地址是怎么映射到物理地址
	通过NAT页表或者段表找到虚拟地址对应的表项,表项中存放的就是逻辑地址到物理	地址的映射


3.互斥量 
#include 
 
int pthread_mutex_destroy(pthread_mutex_t *mutex);          //销毁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);           //初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
     
int pthread_mutex_lock(pthread_mutex_t *mutex);             //上锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);          //尝试上锁     
int pthread_mutex_unlock(pthread_mutex_t *mutex);           //解锁

4. 条件变量
#include 
int pthread_cond_destroy(pthread_cond_t *cond);             //销毁
int pthread_cond_init(pthread_cond_t *restrict cond,
       const pthread_condattr_t *restrict attr);            //初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex,
       const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);.
5.信号量

#include  
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t * sem);
int sem_destroy(sem_t * sem);
调用sem_wait()可以获得资源,使semaphore的值减1,如果调用sem_wait()时semaphore的值已经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait()。调用sem_post()可以释放资源,使semaphore的值加1,同时唤醒挂起等待的线程


6 如何在const成员函数中赋值
答:使用mutable去掉const的成员函数的const性质
cons_cast和mutable的比较
const: 
1)强制去掉对象的const属性
2)缺点,对const对象。调用包含const_cast的const成员函数属于未定义行为
mutable
1) 使用场景:对可能要发生变化的成员前。加上存储描述符mutable
2) 实质:对加了mutable的成员。无视所有const声明

7.为什么函数参数的入栈顺序是从右往左
栈(stack):先进后出
好多函数是不定参数个数的。比如常见的printf.所以需要的参数的入栈顺序从右往左

8.拷贝构造函数作用及用途?什么时候需要自定义拷贝构造函数?
在C++中,有下面三种对象需要拷贝的情况:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化
以上的情况就需要拷贝构造函数的调用。
当类中的数据成员需要动态分配存储空间时,不可以依赖default copy constructor。当default copy constructor被因编译器需要而合成时,将执行default memberwise copy语义。此时如果类中有动态分配的存储空间时,将会发生惨重的灾情。在需要时(包括这种对象要赋值、这种对象作为函数参数要传递、函数返回值为这种对象等情况),要考虑到自定义拷贝构造函数。

9.C++哪些函数不能被声明为虚函数 
1)不能被继承的函数
2)不能被重写的函数
普通函数
友元函数
构造函数
内联成员函数 //待定
静态成员函数

构造函数可以调用虚函数吗?语法上通过吗?语义上可以通过吗?
不能,语法上通过,语义上有问题。
derived class对象内的base class成分会在derived class自身构造之前构造完毕。因此,在base class的构造函数中执行的virtual函数将会是base class的版本,决不会是derived class的版本。
即使目前确实正在构造derived class

当虚函数重载的时候其返回类型必要要么相同要么为协变。

10.深拷贝和浅拷贝的区别
答:浅拷贝:如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会根据需要生成一个默认的拷贝构造函数,完成对象之间的位拷贝。default memberwise copy即称为浅拷贝。
此处需要注意,并非像大多数人认为的“如果class未定义出copy constructor,那么编译器就会为之合成一个执行default memberwise copy语义的copy constructor”。
通常情况下,只有在default copy constructor被视为trivial时,才会发生上述情况。一个class,如果既没有任何base/member class含有copy constructor,也没有任何virtual base class或 virtual functions,
它就会被视为trivial。
通常情况下,浅拷贝是够用的。
深拷贝:然而在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。
这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。如果此时B中执行析构函数释放掉指向那一块堆的指针,这时A内的指针就将成为悬挂指针。
因此,这种情况下不能简单地复制指针,而应该复制“资源”,也就是再重新开辟一块同样大小的内存空间。

11.动态绑定和静态绑定的区别
1对象的静态类型:对象在声明时采用的类型。是在编译期确定的。
2对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
3静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
4动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期

12. virtual函数能声明为内联吗?为什么?
答:通常情况下是不能的
原因:inline是编译期决定,他意味着在执行前就将调用动作替换为被调用函数的本体;
virtual是运行期决定,他意味着直道运行期才决定调用哪个函数。
这两者之间通常是冲突的。

inline 内联函数相比于宏定义的优越之处
inline定于的类的内联函数。函数的代码被放入符号表中。在使用时直接进行替换(像宏一样展开) 。没有了调用的开销。
类的内联函数也是个真正的函数。编译器在调用一个内联函数。首先会检查它的参数的类型。保证调用正确。然后进行一系列的相关检查。就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性
inline 可以作为某个类的成员函数。当然就可以在其中使用所在类的成员函数及私有成员
内联函数与宏定义的区别
1.内联函数在编译时展开。宏在预编译时展开
2. 在编译的时候。内联函数可以直接被镶嵌到目标代码中。而宏只是一个简单的文本替换
3.内联函数可以完成诸如类型监测。语句是否正确等编译功能。宏就不具有这样的功能
4. 宏不是函数。inline函数是函数
5.宏在定义时要小心处理宏参数(一般情况下把参数用括号括起来) 否则容易出现二义性。而内联函数定义时不会出现二义性

内联函数时无法获取其地址的。 
内联函数不可以做为虚函数(内联函数,构造函数,静态函数时都不能为虚函数的)
类中的构造函数和析构函数可以作为内联函数


13 qt process的使用方式

当程序路径中含有空格的时候,仅设置程序路径program是无法被识别的。解决方法是再将程序路径传递给arguments
   外部程序未启动时。其状态是NotRunning;
    当启动时,其状态转变为Starting,正在启动。但此时还未调用起来。
    启动之后,继续变为Running,同一时候发射出started()信号,此时,能够对QProcess进行读写操作了。
    当退出时,其状态改为NotRunning。并发射出finished()信号。finishe()信号会携带退出码和退出状态,能够分别通过exitCode()和exitStatus()来获得。
    当错误发生时,QProcess会发出一个error()信号。相同的。也能够通过error()来获得其错误类型,通过state()获得当前程序的状态。
QProcess提供了一系列的函数以提到事件循环来完毕同步操作:
(1)waitForStarted()          : 堵塞。直到外部程序启动
(2)waitForReadyRead()    : 堵塞,直到输出通道中的新数据可读
(3)waitForBytesWritten()  : 堵塞,直到输入通道中的数据被写入
(4)waitForFinished()        : 堵塞,直到外部程序结束
 meetingProcess->start(program,QStringList()<waitForStarted(5000);
 if(meetingProcess->state() != QProcess::Running){
      QMessageBox::information(this,QString::fromLocal8Bit("提示"),QString::fromLocal8Bit("会议启动失败"),QMessageBox::Default);
 }
qt 实现启动一个进程示例
 QSystemSemaphore sema("ibuguKey",1,QSystemSemaphore::Open);
    sema.acquire();

    QSharedMemory mem("ibuguObj");
    bool isRunning = false;
    if(mem.attach()){
        isRunning = true;
    }
    else{
        mem.create(1);
        isRunning = false;
    }

    sema.release();

    if(isRunning){
        QMessageBox::information(NULL,QString::fromUtf8("提示"),QString::fromLocal8Bit("The application is already running."),QMessageBox::Default);
        return -1;
    }

14. RTMP/RTSP/HTTP
https://www.cnblogs.com/gongyuhonglou/p/5605320.html
https://www.jianshu.com/p/c2284659452f

RTMP和RTSP协议是流媒体协议。
1:RTMP协议是Adobe的私有协议,未完全公开,RTSP协议和HTTP协议是共有协议,并有专门机构做维护。
2:RTMP协议一般传输的是flv,f4v格式流,RTSP协议一般传输的是ts,mp4格式的流。HTTP没有特定的流。
3:RTSP传输一般需要2-3个通道,命令和数据通道分离,HTTP和RTMP一般在TCP一个通道上传输命令和数据。
1:RTSP实时流协议
作为一个应用层协议,RTSP提供了一个可供扩展的框架,它的意义在于使得实时流媒体数据的受控和点播变得可能。总的说来,RTSP是一个流媒体表示 协议,主要用来控制具有实时特性的数据发送,但它本身并不传输数据,而是必须依赖于下层传输协议所提供的某些服务。RTSP可以对流媒体提供诸如播放、暂 停、快进等操作,它负责定义具体的控制消息、操作方法、状态码等,此外还描述了与RTP间的交互操作(RFC2326)。
2:RTCP控制协议
RTCP控制协议需要与RTP数据协议一起配合使用,当应用程序启动一个RTP会话时将同时占用两个端口,分别供RTP和RTCP使用。RTP本身并 不能为按序传输数据包提供可靠的保证,也不提供流量控制和拥塞控制,这些都由RTCP来负责完成。通常RTCP会采用与RTP相同的分发机制,向会话中的 所有成员周期性地发送控制信息,应用程序通过接收这些数据,从中获取会话参与者的相关资料,以及网络状况、分组丢失概率等反馈信息,从而能够对服务质量进 行控制或者对网络状况进行诊断。
RTCP协议的功能是通过不同的RTCP数据报来实现的,主要有如下几种类型:
SR:发送端报告,所谓发送端是指发出RTP数据报的应用程序或者终端,发送端同时也可以是接收端。(SERVER定时间发送给CLIENT)。
RR:接收端报告,所谓接收端是指仅接收但不发送RTP数据报的应用程序或者终端。(SERVER接收CLIENT端发送过来的响应)。
SDES:源描述,主要功能是作为会话成员有关标识信息的载体,如用户名、邮件地址、电话号码等,此外还具有向会话成员传达会话控制信息的功能。
BYE:通知离开,主要功能是指示某一个或者几个源不再有效,即通知会话中的其他成员自己将退出会话。
APP:由应用程序自己定义,解决了RTCP的扩展性问题,并且为协议的实现者提供了很大的灵活性。
3:RTP数据协议
RTP数据协议负责对流媒体数据进行封包并实现媒体流的实时传输,每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前12个字节的含义是固定的,而负载则可以是音频或者视频数据。
RTP用到的地方就是 PLAY ,服务器往客户端传输数据用UDP协议,RTP是在传输数据的前面加了个12字节的头(描述信息)。
RTP载荷封装设计本文的网络传输是基于IP协议,所以最大传输单元(MTU)最大为1500字节,在使用IP/UDP/RTP的协议层次结构的时候,这 其中包括至少20字节的IP头,8字节的UDP头,以及12字节的RTP头。这样,头信息至少要占用40个字节,那么RTP载荷的最大尺寸为1460字 节。以H264 为例,如果一帧数据大于1460,则需要分片打包,然后到接收端再拆包,组合成一帧数据,进行解码播放。

RTSP:options.describe.setup play pause .teardown
消费分为两大类 。一是请求消息。一是回应消息。两种消息的格式不同
请求消息
方法 URL RTSP版本 CR LF
消息头 CR LF CT LF
消息体 CR LF
回应消息
RTSP版本 状态码 解释 CR LF
消息头CR LF CR LF
消息体 CR LF
其中RTSP版本一般都是RTSP/1.0.状态码是一个数值。200表明成功。解释是与状态码对应的文本解释
RTP标准定义了两个子协议 RTP /RTCP
数据传输协议RTP.用于实时传输数据。该协议提供的信息包括:时间戳。序列号(用于丢包和重排序检测) 以及负载格式(用于数据的编码格式)
控制协议RTCP 用于Qos反馈和同步媒体流。相对于RTP来说。RTCP所占宽带比较小


vector 知识点:
push_back()函数向容器中加入一个临时对象(右值元素)时, 首先会调用构造函数生成这个对象,然后条用拷贝构造函数将这个对象放入容器中, 最后释放临时对象。但是emplace_back()函数向容器中中加入临时对象, 临时对象原地构造,没有赋值或移动的操作。

函数重载
两个函数成为重载函数,必须具有如下特征:
函数的重载的规则:
函数名称必须相同。
参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
函数的返回类型可以相同也可以不相同。
仅仅返回类型不同不足以成为函数的重载

const char* char const* char const*区别
const char* 相当于char const* 一个指针。指向的的内容是const char 因此指针可以指向其他地址。但是无法通过指针修改指向位置的值
char* const 一个const指针。指针的值无法改变。但是指针指向的内容可以改变

识别函数和指针
void * ( * (*fp1)(int))[10]; 
float (*(* fp2)(int,int,int))(int);
int (* ( * fp3)())[10](); 
分别表示什么意思?
【标准答案】                                                           
1、void * ( * (*fp1)(int))[10];  fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,
这个数组有10个元素,每个元素是一个void*型指针。
2、float (*(* fp2)(int,int,int))(int);  fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,
这个函数的参数为int型,函数的返回值是float型。
3、int (* ( * fp3)())[10]();  fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,
这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。

多态类中的虚函数表是 Compile-Time,还是 Run-Time 时建立的
虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组.而对象的隐藏成员--虚拟函数表指针是
在运行期--也就是构造函数被调用时进行初始化的,这是实现多态的关键

全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的
生命周期不同:
全局变量随主程序创建和创建,随主程序销毁而销毁;
局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在; 内存中分配在全局数据区。 

使用方式不同:
通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。
操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 


std 自旋锁

函数指针与指针函数
二者区别:
指针函数本质是一个函数,其返回值为指针
函数指针本质是一个指针,其指向一个函数
写法不同:
指针函数:int* fun(int,int)
函数指针:int (*fun)(int,int)


C++类与类之间的关系
https://blog.csdn.net/qq_39551987/article/details/80730664
1. 集成关系
2. 实现关系
3. 依赖关系
4. 聚合关系
5. 关联关系
6. 组合关系

C++ 构造函数与成员变量的生成顺序
http://blog.sina.com.cn/s/blog_80ce3a550102wmlb.html
变量的初始化顺序就应该是:
1 基类的静态变量或全局变量
2 派生类的静态变量或全局变量
3 基类的成员变量
4 派生类的成员变量

注意:类中const成员常量必须在构造函数初始化列表中初始化
注意:类中static成员变量,必须在类外初始化。
C++ 类成员的构造和析构顺序
对象并不是突然建立起来的,创建对象必须时必须同时创建父类以及包含于其中的对象。C++遵循如下的创建顺序:
(1)如果某个类具体基类,执行基类的默认构造函数。
(2)类的非静态数据成员,按照声明的顺序创建。
(3)执行该类的构造函数。
即构造类时,会先构造其父类,然后创建类成员,最后调用本身的构造函数。

如果在构造函数内调用了自身的虚函数, 这类调用是不会重定向到子类的虚函数实现. 即使当前没 有子类化实现, 将来仍是隐患. 

 





因为文章内涉及到链接。如果说侵犯了他人利益请及时联系我。

你可能感兴趣的:(c++,C++面试题,C++基础知识点,全面知识点)