标签:
目录
1. 多态性都有哪些?动态绑定怎么实现? 3
2. 虚函数,虚函数表里面内存如何分配? 3
3. 纯虚函数如何定义,为什么析构函数要定义成虚函数? 4
4. C++中哪些不能是虚函数? 4
5. 类型转换有哪些? (参考) 4
6. 为什么要用static_cast转换而不用c语言中的转换? 4
7. 操作符重载(+操作符),具体如何去定义? 5
8. 内存对齐的原则? 5
9. 内联函数与宏定义的区别? 5
10. 关键字static、extern、const、typedef 5
11. C++中调用被C编译器编译后的函数,为什么加extern “C”? 5
12. 如何实现只能动态分配类对象,不能定义类对象?参考 6
13. explicit是干什么用的 6
14. 内存溢出有那些因素? 7
15. new与malloc的区别,delet和free的区别?参考 7
16. 必须使用【初始化列表】初始化数据成员的情况 8
17. C++引用和指针的区别? 8
18. 说说栈区和堆区和静态存储区?参考 8
19. auto_ptr类,C++智能指针的实现? 9
20. 迭代器删除元素的会发生什么? 迭代器失效?未完 9
21. 内存溢出有那些因素?未完 10
22. 模版怎么实现???未完 10
23. 模版特化的概念,为什么特化?????未完 10
24. 静态成员函数和数据成员有什么意义?????未完 10
25. 异常机制是怎么回事? 11
26. 最近的一个项目,技术难点啊,怎么克服的? 11
27. ===============操作系统相关=================== 11
28. linux进程、线程总结?参考 11
29. 线程同步方式 12
30. 进程通信方式? 13
31. 产生死锁的四个条件,以及它们的处理方法? 未完 13
32. IO多路复用有epoll, poll, select?Linux网络编程,不熟悉 13
33. Linux常用命令? 《鸟哥Linux私房菜》 14
34. 哈希表?笔记本问题25 14
35. 数据库事务? 14
36. ================数据结构和算法================= 14
37. 查找数组中第k大的数字?Offer的30题。 14
38. 排序算法有哪些?快速排序实现?最好时间复杂度,平均时间复杂度? 15
39. strcpy函数的编写? strcpy返回类型是干嘛用的? 15
40. STL有哪些容器,对比vector和set? 15
41. 红黑树的定义和解释?B树,B+树的基本性质? 15
42. ==============网络以及其它==================== 15
43. 常见的加密算法? 15
44. https? 15
多态性是一个接口,多种实现,是面向对象的核心。 编译时多态性:通过重载函数实现。运行时多态性:通过虚函数实现,结合动态绑定。
通过基类的引用或指针调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。
动态绑定是通过虚函数实现的。而虚函数是通过一张虚函数表实现的。
用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
存在虚函数的类都有一个一维的虚函数表叫做虚表。类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
编译器在编译的时候,发现animal类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表,该表是个一维数组,数组中存放每个虚函数的地址。animal和fish类都包含了一个虚函数breathe(),因此编译器会为这两个类都建立一个虚表。
那么虚表指针在什么时候,或者说在什么地方初始化呢?
答案是在构造函数中进行虚表的创建和虚表指针的初始化。还记得构造函数的调用顺序吗,在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否后还有继承者,它初始化父类对象的虚表指针,该虚表指针指向父类的虚表。当执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。
总结(基类有虚函数):
1. 每一个类都有虚表。
2. 虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。如果派生类有自己的虚函数,那么虚表中就会添加该项。
3. 派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。
一般情况下,当我们用一个指针/引用调用一个函数的时候,被调用的函数是取决于这个指针/引用的类型。即如果这个指针/引用是基类对象的指针/引用就调用基类的方法;如果指针/引用是派生类对象的指针/引用就调用派生类的方法,当然如果派生类中没有此方法,就会向上到基类里面去寻找相应的方法。这些调用在编译阶段就确定了。当涉及到多态性的时候,采用了虚函数和动态绑定,此时的调用就不会在编译时候确定而是在运行时确定。不在单独考虑指针/引用的类型而是看指针/引用的对象的类型来判断函数的调用,根据对象中虚指针指向的虚表中的函数的地址来确定调用哪个函数。
http://blog.csdn.net/haoel/article/details/1948051
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。纯虚函数是虚函数再加上= 0。virtual void fun ()=0。
在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。
Base *p =new son;
delete p;
如果析构函数不是虚函数,那么释放内存时候,编译器会使用静态联编,认为p就是一个基类指针,调用基类析构函数,这样子类对象的内存没有释放,造成内存泄漏。定义成虚函数以后,就会动态联编,先调用子类析构函数,在基类。
1)普通函数只能overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时绑定函数。
2)构造函数是知道全部信息才能创建对象,然而虚函数允许只知道部分信息。
3)内联函数在编译时被展开,虚函数在运行时才能动态的邦定函数。
4)友元函数 因为不可以被继承。
5)静态成员函数 只有一个实体,不能被继承。父类和子类共有。
? 静态类型转换,static_cast,基本类型之间和具有继承关系的类型。
例子A,double类型转换成int。B,将子类对象转换成基类对象。
? 常量类型转换,const_cast, 去除指针变量的常量属性。
无法将非指针的常量转换为普通变量。
? 动态类型转换,dynamic_cast,运行时进行转换分析的,并非在编译时进行。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast用于类层次间的向上转换和向下转换,还可以用于类间的交叉转换。在类层次间进行向上转换,即子类转换为父类,此时完成的功能和static_cast是相同的,因为编译器默认向上转换总是安全的。向下转换时,dynamic_cast具有类型检查的功能,更加安全。类间的交叉转换指的是子类的多个父类之间指针或引用的转换。
该函数只能在继承类对象的指针之间或引用之间进行类型转换。或者有虚函数的类。两种
例子,A派生类指针转换为基类指针。B没有继承关系,但被转换类有虚函数
? 重定义类型转换,一个类型的指针转换为另一个类型的指针。
Static_cast转换,它会检查类型看是否能转换,有类型安全检查。
比如,这个在C++中合法,但是确实错误的。
A* a= new A;
B* b = (B*)a;
除了类属关系运算符”.”、成员指针运算符”.*”、作用域运算符”::”、sizeof运算符和三目运算符”?:”以外,C++中的所有运算符都可以重载。
<返回类型说明符> operator <运算符符号>(<参数表>){}
重载为类的成员函数和重载为类的非成员函数。参数个数会不同,应为this指针。
A.结构体的大小为最大成员的整数倍。
B.成员首地址的偏移量为其类型大小整数倍。
在程序编译时,不在单独长生代码,将内联函数调用处用函数体替换,这一点类似于C语言中的宏扩展。
动机:是用来消除函数调用时的时间开销。频繁被调用的短小函数非常受益。
A, 宏定义不检查函数参数,返回值什么的,只是展开,相对来说,内联函数会检查参数类型,所以更安全。
B. 宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的
Const 推出的初始目的,为了取代预编译指令,消除它的缺点同时继承它的优点。
const用法1:常量。用法2:指针和常量。用法3:const修饰成员函数(c++特性)
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;
const成员函数不可以修改对象的数据,不管对象是否具有const性质。编译时以是否修改成员数据为依据进行检查。
Const 实现的原理是什么?不完整 参考
有下面两种情况,会为这个变量分配存储空间:
1、当const常量为全局,并且需要在其它文件中使用时,(extern)
2、当使用取地址符(&)取const常量的地址时。
在C++中,被const修饰的变量,可能为其分配存储空间,也可能不分配存储空间。
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
typedef char Line[81]; int (*PF)(int , int);
typedef int (*PF)(int , int);
C+支持函数重载,而C不支持,函数被C+编译和C编译后在库中的名字不同。例如,void foo(int a,int b),C中可能是_foo,而C+中就是foo_int_int。
此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。解决名字匹配问题。
动态分配就是用运算符new来创建一个类的对象,在堆上分配内存。
静态分配就是A a;这样来由编译器来创建一个对象,在栈 上分配内存。
为什么要用static?
1 构造器 ,2 默认且隐含的类型转换操作符。
explicit构造函数是用来防止隐式转换的
struct A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
operator bool() const { return true; }
};
struct B
{
explicit B(int) { }
explicit B(int, int) { }
explicit operator bool() const { return true; }
};
int main()
{
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
if (b1) break; // OK: B::operator bool()
// bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
bool nb2 = static_cast(b2); // OK: static_cast performs direct-initialization
}
有过C++开发经验的人会发现,我们可以将0作为false,非零作为true。一个函数即使是bool类型的,但是我们还是可以返回int类型的,并且自动将0转换成false,非零转换成true。 C++非类型安全,Java类型安全。
(1) 使用非类型安全(non-type-safe)的语言如 C/C++ 等。
(2) 以不可靠的方式存取或者复制内存缓冲区。
(3) 编译器设置的内存缓冲区太靠近关键数据结构。
void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
void free(void *FirstByte): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
new的时候会有两个事件发生:1).内存被分配(通过operator new 函数) 2).为被分配的内存调用一个或多个构造函数构建对象。
delete的时候,也有两件事发生:1).为将被释放的内存调用一个或多个析构函数 2).释放内存(通过operator delete 函数)。
区别:
1.malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符
2.new能够自动分配空间大小,malloc传入参数。
3.对于用户自定义的对象而言,用maloc/free无法满足动态管理对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++需要一个能对对象完成动态内存分配和初始化工作的运算符new,以及一个能对对象完成清理与释放内存工作的运算符delete—简而言之 new/delete能进行对对象进行构造和析构函数的调用进而对内存进行更加详细的工作,而malloc/free不能。
既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
情况一、是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化); 情况一的说明:数据成员是对象,并且这个对象只有含参数的构造函数,没有无参数的构造函数;
情况二、const修饰的类成员;
情况三、引用成员数据;
这里再强调一下类的初始化的顺序,应该是类成员变量的初始化不是按照初始化表的顺序被初始化的,而是按照在类中声明的顺序被初始化的。
1.定义不同。指针是一个变量,这个变量存储的是一个地址;而引用原变量的别名。
2能不能改变。指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
3能不能为空。指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
4. 指针可以有多级,但是引用只能是一级。
5 sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
18. 说说栈区和堆区和静态存储区?参考
(1). 管理方式:栈资源由编译器自动管理。堆中资源由程序员控制。
(2). 空间大小不同
(3). 能否产生碎片不同
(4). 生长方向:栈是向低地址生长的连续空间,堆是向高地址生长不连续空间。
(5). 分配方式不同
(6). 分配效率不同
内存的静态分配和动态分配的区别主要是两个:
一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。
二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。alloca,可以从栈里动态分配内存,不用担心内存泄露问题,因为在函数A返回时,通过alloca申请的内存就会被自动释放掉。
首先对于vector而言,添加和删除操作可能使容器的部分或者全部迭代器失效。那为什么迭代器会失效呢?vector元素在内存中是顺序存储。当新添加一个元素时候,没有地方存放,于是vector必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间的元素被复制到新的存储空间里,接着插入新的元素,最后撤销旧的存储空间。这种情况发生,一定会导致vector容器的所有迭代器都失效。
vector迭代器的几种失效的情况:
1、当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2、当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。
3、当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。
1、对于节点式容器(map, list, set)元素的删除,插入操作会导致指向该元素的迭代器失效,其他元素迭代器不受影响。
2、对于顺序式容器(vector)元素的删除、插入操作会导致指向该元素以及后面的元素的迭代器失效。
21. 内存溢出有那些因素?未完
有过C++开发经验的人会发现,我们可以将0作为false,非零作为true。一个函数即使是bool类型的,但是我们还是可以返回int类型的,并且自动将0转换成false,非零转换成true。 C++非类型安全,Java类型安全。
(1) 使用非类型安全(non-type-safe)的语言如 C/C++ 等。
(2) 以不可靠的方式存取或者复制内存缓冲区。
(3) 编译器设置的内存缓冲区太靠近关键数据结构。
template void swap(T& a, T& b){}
模板是一种对类型进行参数化的工具;是对类型的抽象。
模板的特化是在已有的通用模板不再适用于一些特殊的类型参数时,而针对这些特殊的类型参数专门实现的模板。
全特化,就是模板中模板参数全被指定为确定的类型。
偏特化,就是模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。
我们也可以将T特化为 T*,const T*, T&, const T&等
template
class stack {};
template < >
class stack { //…// };
上述定义中template < >告诉编译器这是一个特化的模板。
E 静态成员函数没有this指针,它不能返回非静态成员,因为除了对象会调用它外,类本身也可以调用。静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。
F 非静态成员函数可以任意地访问静态成员函数和静态数据成员。
G 静态成员函数不可以同时声明为 virtual、const、volatile函数。
H.静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用类成员函数指针来储存。
注意:static 成员变量的内存空间既不是在声明类时分配,也不是在创建对象时分配,而是在初始化时分配。static 成员变量与对象无关,不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。static 成员变量和普通 static 变量一样,编译时在静态数据区分配内存,到程序结束时才释放。
static 成员变量既可以通过对象来访问,也可以通过类来访问。通过类来访问的形式为:类名::成员变量; 对象.成员变量
区别 进程 线程
概念 进程是具有一定独立功能的程序关于某个数据集合上的一次运行。进程是系统资源分配和调度的基本单位。 线程体现的特征是可执行的,是CPU调度和分派的基本单位。
关系 一个进程可以有多个线程,但至少有一个线程。 一个线程线程必定是属于某个进程
资源
分配 资源只分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。当进程结束时,所有的资源被回收。 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(线程ID,程序计数器,一组寄存器和栈)。
系统
开销 创建或撤消时,系统都要为之分配或回收资源,如内存空间、I/o设备等。在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。 线程创建和切换只须保存和设置少量寄存器的内容,并 不涉及存储器管理方面的操作。
通信 进程间通信:消息队列,共享内存。信号量,信号,套接字,管道。 共享进程资源可直接访问。
同步 进程同步实际上是指不同进程中的线程同步。
注:某些同步方式不能跨进程,如临界区。 互斥,信号量,事件,条件变量,
临界区,读写锁等。
并发性 线程能让系统有更高的并发性。
作业:用户在一次事务处理中要求计算机系统所做的工作的集合。
进程:一个程序在一个数据集合上的一次运行过程。
线程:进程中的一个实体。
管程:一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。
1) 临界区Critical Section在任意时刻只允许一个线程对共享资源进行访问。
2) 互斥量:跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。
3) 信号量:号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。P操作 申请资源V操作 释放资源。
4) 事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。
同步方式 线程 进程 linux内核
临界区 速度快,适合控制数据访问问。 × ×
互斥 此3中同步方式可用于同一个进程的线程同步,也可以用于不同进程中的线程同步。信号量与其它2个不同的是可同时运行多个线程。
注:此处说的进程同步实际上是指不同进程中的线程同步。 ×
事件 ×
信号量 长期加锁
持有锁是需要睡眠、调度
注:与进程线程的信号量不同。
自旋锁 × × 低开销加锁
短期锁定
中断上下文中加锁
线程间通信
*锁机制:包括互斥锁、条件变量、读写锁。
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
* 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
* 信号机制(Signal):类似进程间的信号处理
1) 管道通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2) 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
3) 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。主要作为进程间以及同一进程不同线程之间的同步手段。
4) 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式
5) 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
6) 套接字:与其他通信机制不同的是,它可用于不同机器间的进程通信。
31. 产生死锁的四个条件,以及它们的处理方法? 未完
1)互斥条件,一个资源一次只能一个进程使用。
2)不可剥夺条件,进程已经获得的资源,在没使用完之前,不可强行剥夺。
3)请求保持条件,一个进程因为请求资源而阻塞的时候,对已经获得的保持不放。
4)循环等待条件,多个进程形成一种收尾相接的等待关系。
互斥条件无法被破坏,只能处理另外的三个
避免死锁:银行家算法。
同一个线程内同时处理多个IO请求的目的。
前面两个是轮询,后面一个是回调,所以效率高。1是数组实现。2是链表实现的3是用了一个红黑树。而且1,2都有用户态到内核态的拷贝过程,3则没有,因为它使用的是共享内存机制。
? 文件系统结构和基本操作
ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …
? 学会使用一些文本操作命令 sed/awk/grep/tail/less/more …
? 学会使用一些管理命令
ps/top/lsof/netstat/kill/tcpdump/iptables/dd…
是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
ACID(原子性、一致性、隔离性和持久性)
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
对称式加密就是加密和解密使用同一个密钥。
非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为“公钥”和“私钥”,它们两个必需配对使用,否则不能打开加密文件。
DES(Data Encryption Standard):对称算法,数据加密标准,速度较快,适用于加密大量数据的场合;
MD5的典型应用是对一段Message产生fingerprint(指纹),以防止被“篡改”。
RSA是第一个既能用于数据加密也能用于数字签名的算法。
HTTP下加入SSL层,HTTPS的安全基础是SSL。
标签:
原文地址:http://blog.csdn.net/u014463687/article/details/51767737