Q: 说一下static关键字的作用
A:
- 全局静态变量
在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。
- 内存中的位置:静态存储区;
- 作用时间:在整个程序运行期间一直存在;
- 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
- 作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
- 局部静态变量
在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。
- 内存中的位置:静态存储区
- 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
- 作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
静态函数
在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。
函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;
warning:不要在头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;类的静态成员
在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用类的静态函数
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);
Q: 说一说c++中四种cast转换
A:
C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast
const_cast
用于将const变量转为非conststatic_cast
静态类型转换,运行时不做类型检查来保证安全性, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知;dynamic_cast
用于动态类型转换,运行时检查类型安全(转换失败返回null)。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。要深入了解内部转换的原理。
向上转换:指的是子类向基类的转换
向下转换:指的是基类向子类的转换
它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。reinterpret_cast
几乎什么都可以转,比如将int转指针,可能会出问题,尽量少用;为什么不使用C的强制转换?
C的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错。
Q: 请你说一下你理解的c++中的smart pointer四个智能指针: shared_ptr,unique_ptr,weak_ptr,auto_ptr
A:
- auto_ptr(c++98的方案,cpp11已经抛弃)
1 | auto_ptr<string> p1 (new string ("I reigned lonely as a cloud.")); |
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题!
- unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。
采用所有权模式,还是上面那个例子
1 | unique_ptr<string> p3 (new string ("auto")); //#4 |
编译器认为p4=p3非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全。
另外unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:
1 | unique_ptr<string> pu1(new string ("hello world")); |
其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。
注:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。例如:
1 | unique_ptr<string> ps1, ps2; |
shared_ptr
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。
shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个shared_ptr管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr设计的目的是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作, 它只可以从一个shared_ptr或另一个weak_ptr对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
Q: C++静态多态和动态多态?如何实现动态多态?
A:静态多态在C++中有两个体现:
模板。当需要不同类型的对象的时候,使用泛型来代替具体的数据类型。在编译阶段将泛型替换为具体的数据类型,使编译器生成该类型的函数
函数重载。函数名相同而参数不同的一系列函数为重载函数,编译器在编译阶段通过传递的参数类型确定具体调用哪一个函数。
动态多态即是在程序运行阶段判断对象的具体类型,从而调用实际的方法。动态多态的实现有两个条件:
- 子类重写父类的虚函数。
- 通过父类的指针或引用调用子类的虚函数。
Q: 说一说C++11新特性?
A:
- auto关键字——自动类型推导
- for each循环——简洁的循环语句
- 初始化列表——统一的初始化语法
- 委托构造——一个类中构造函数调用另一个构造函数,从而简化代码
- Lambda表达式——匿名函数
- 右值引用——(至今未搞懂)
Q: 在浏览器输入url后发生了什么
一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
主要包括以下几个基本步骤:
- 浏览器的地址栏输入URL并按下回车。
- 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
- DNS解析URL对应的IP。
- 根据IP建立TCP连接(三次握手)。
- HTTP发起请求。
- 服务器处理请求,浏览器接收HTTP响应。
- 渲染页面,构建DOM树。
- 关闭TCP连接(四次挥手)。
接下来对其中几个步骤展开说一下
Q:Linux的kill
命令和kill -9
有什么区别?
A:两个命令都是用来杀死进程。
其中不加参数的kill
命令会默认发送信号15,也就是kill -15
,被称为优雅的退出。
当使用kill -15时,系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该信号后,具体要如何处理是自己可以决定的。
这时候,应用程序可以选择:
1、立即停止程序
2、释放响应资源后停止程序
3、忽略该信号,继续执行程序
因为kill -15
信号只是通知对应的进程要进行”安全、干净的退出”,程序接到信号之后,退出前一般会进行一些”准备工作”,如资源释放、临时文件清理等等,如果准备工作做完了,再进行程序的终止。但是,如果在”准备工作”进行过程中,遇到阻塞或者其他问题导致无法成功,那么应用程序可以选择忽略该终止信号。
kill -9
信号则会发出SIGKILL信号,告诉进程你已经被终结了,程序会直接退出。该信号要求接收到该信号的程序应该立即结束运行,不能被阻塞或者忽略。