Q:主要语言是C++是吧,那我们先来考察一下C++的基础。
A: 好的。
Q:C++里常量的作用是什么?是怎么定义常量的?常量存放在内存的哪个位置?
1:增强程序的可读性。用一个有意义的常量字符串代替一个常数,程序读起来会更加的方便。
2:如果很多地方用到像PI(3.14159)这样的常量,可以一改改全局。
常量是在程序运行中不能被改变的标识符。
C++中定义常量可以用#define 、const 这两种方法。
例如:
#define PRICE 10 //定义单价常量10
const int PRICE = 10; //定义单价常量10
区别:
其中#define是定义宏变量,它其实是在编译之前,由预处理指令把代码里面的宏变量用指定的字符串替换,它不做语法检查,而constant 则是定义含有变量类型的常量。
一般说来推荐使用constant定义常量,它在编译时会做语法检查。Effective c++ 的条款1中:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。而且有时候用宏,会出现意想不到的输出结果。
两者比较:
(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。
(2) 有些集成化的调试工具可以对 const 常量进行调试, 但是不能对宏常量进行调试。
常量定义必须初始化。对于局部对象,常量存放在栈区,对于全局对象,常量存放在全局/静态存储区。对于字面值常量,常量存放在常量存储区。
Q:你刚刚说到了const,const修饰成员函数的目的是什么?
A:const修饰的成员函数表明函数调用不会对对象做出任何更改,事实上,如果确认不会对对象做更改,就应该为函数加上const限定,这样无论const对象还是普通对象都可以调用该函数。
Q:那如果同时定义了两个函数,一个带const,一个不带,会有问题吗?
A:不会,这相当于函数的重载。
Q:C++ 类内可以定义引用数据成员吗?
A:可以,必须通过成员函数初始化列表初始化。
最近才发现C++可以定义引用类型的成员变量,以前一直以为不可以,原因是初始化时有编译错误,现在说明一下如何定义并初始化引用类型的成员变量:
class Test
{
private:
int &a;//引用类型的成员变量
public:
Test(int &b)
{
a = b;
}
};
上面的写法是有问题的,原因是引用类型的成员变量必须在构造函数的初始化列表中进行初始化,因此正确的写法是:
class Test
{
private:
int &a;
public:
Test(int &b) : a(b)//注意写法
{
}
};
//这样,成员变量a就会被初始化为b的引用了。
下面我们来做一个测试:
#include <iostream>
using namespace std;
class Test
{
private:
int &a;
public:
Test(int &b) : a(b)
{
}
void Modify(int value)
{
a = value;
}
};
int main()
{
int b = 3;
Test test(b);
cout <<"b="<<b<<endl;
test.Modify(4);
cout <<"b="<<b<<endl;
return 0;
}
输出结果为:
b=3
b=4
由此可见,Test类中的变量a成功引用了main中的变量b
Q:new/delete与malloc/free的区别是什么?
A:首先,new/delete是C++的关键字,而malloc/free是C语言的库函数,后者使用必须指明申请内存空间的大小,对于类类型的对象,后者不会调用构造函数和析构函数。
Q:你博客里提到了隐式类型转换,能简单说说吗?
A:首先,对于内置类型,低精度的变量给高精度变量赋值会发生隐式类型转换,其次,对于只存在单个参数的构造函数的对象构造来说,函数调用可以直接使用该参数传入,编译器会自动调用其构造函数生成临时对象。
Q:如何避免?
A:explicit关键字。防止只有一个参数(或仅有唯一一个非默认参数)的构造函数隐式类型转换 详细了解博客:https://blog.youkuaiyun.com/yhc166188/article/details/79729852
Q:说说你了解的类型转换。
A:C++类型转换有四种。详细了解博客:https://blog.youkuaiyun.com/yhc166188/article/details/79729852
Q:说说reinterpret_cast.
A: 这种是不常用的,可以用于任意类型的指针之间的转换,对转换的结果不做任何保证,可能被用于哈希(此处不确定)。
Q:说说dynamic_cast
A: 这种其实也是不被推荐使用的,更多使用static_cast,dynamic本身只能用于存在虚函数的父子关系的强制类型转换,对于指针,转换失败则返回nullptr,对于引用,转换失败会抛出异常。
Q:说说const_cast
A:事实上的用途还是很普遍的。对于未定义const版本的成员函数,我们通常需要使用const_cast来去除const引用对象的const,完成函数调用。另外一种使用方式,结合static_cast,可以在非const版本的成员函数内添加const,调用完const版本的成员函数后,再使用const_cast去除const限定。
Q:说说你了解的RTTI.
A:运行时类型检查,在C++层面主要体现在dynamic_cast和typeid.RTTI(Run Time Type Identification)即通过运行时类型识别,程序能够使用基类的指针或引用来检查着这些指针或引用所指的对象的实际派生类型。
可以了解博客:https://blog.youkuaiyun.com/three_bird/article/details/51479175
Q:具体是怎么实现的。
A:VS中虚函数表的-1位置存放了指向type_info的指针。对于存在虚函数的类型,typeid和dynamic_cast都会去查询type_info.
Q:你刚刚提到虚函数表,具体是怎样实现运行时多态的。
A:简单来讲,子类若重写父类虚函数,虚函数表中,该函数的地址会被替换,对于存在虚函数的类的对象,在VS中,对象的对象模型的头部存放指向虚函数表的指针,通过该机制实现多态。
Q:C++函数栈空间的最大值 ?
A:默认是1M,不过可以调整。
Q:extern “C” ?
A:C++调用C函数需要extern C,因为C语言没有函数重载。
Q:设计模式了解吗,介绍一下单例模式。
A:C++的实现有两种,一种通过局部静态变量,利用其只初始化一次的特点,返回对象。另外一种,则是定义全局的指针,getInstance判断该指针是否为空,为空时才实例化对象。
Q:你说的第二种就是所谓的懒加载。现在有一个问题,如果并发访问,该怎么做。
A:使用锁机制,防止多次访问。
Q:你的锁是锁住所有的代码吗?
A:是。
Q:这样会多次重复判断是否为空,而每次都会加锁,有什么办法改善?
A:可以这样,第一次判断为空不加锁,若为空,再进行加锁判断是否为空,若为空则生成对象。
Q:你提到了锁机制,那么C++的锁你知道几种。
A:锁包括互斥锁,条件变量,自旋锁和读写锁(后两个没答出来)。
Q:说一说你用到的。
A:生产者消费者问题利用互斥锁和条件变量可以很容易解决,条件变量这里起到了替代信号量的作用。balabala。
Q:C++两种map。
A:unordered_map(哈希表)和map(红黑树)。
Q:红黑树了解吗?
A:只知道本质是一颗BST,插入O(logN)。
Q: 快排的时间复杂度最差是多少?
A:O(N2)
Q:什么时候最差?
A:枢纽元左侧元素都比枢纽元小(不确定)。
Q:稳定排序哪几种?为什么?
A:查课本吧,很简单。
Q:聊聊计算机网络的内容吧,TCP三次握手是怎样的?
A:这部分大家参考相关课本。
Q:为什么两次不可以?
A:服务器端未收到客户端的连接确认,因此必须三次。
Q:四次呢?
A:连接确认后第三次的握手已经包含了客户端数据,不需要四次。
Q:TCP拥塞了解吗。
A:没看。(直接跳过了这部分)。
Q:死锁产生的必要条件?
A: 四个必要条件。
Q:如何预防?
A:-请查书。
Q:最后几个问题,你平时如何提升自己的,在语言方面?
A:一些经典的书籍,会认真去看,同时,也会看一些对语言新特性介绍的书籍,保持敏感。
Q:分别有哪些书?
A:C++ primer,经典的入门书,深入探索C++对象模型,帮助我很好的理解C++的对象模型。effective C++,经典的关于C++进阶的使用经验。effective modern C++:关于C++11/14新特性的深入探讨。
Q:说说C++primer中你觉得感受最深刻的内容。
A:这本书是入门必读,我感觉印象最深刻的就是C++ 11的新特性,比如自动类型推导,lambda,右值引用(这里被打断了)。
Q:现在在看哪些书?
A:正在看的是STL源码剖析。
C语言是怎么进行函数调用的?
A:每一个函数调用都会分配函数栈,在栈内进行函数执行过程。
接着:C语言参数压栈顺序?
A:从右到左
再来:C语言如何处理返回值?
A:C语言不知道,C++会生成一个临时变量,把它的引用作为函数参数传入函数内。
问题2:
C++如何处理内存泄漏?
A:紧张地瞎说了一堆,提醒大家,不会的直接说不会。
问题3:
C++ 如何处理程序异常?
A:查看对应的报错代码,根据代码定位具体问题。(瞎扯)
问题4:
工厂模式?优点?
A:优点就是解耦,代码复用,更改功能容易。
总结:不会的不要说,直接说不会,楼主一个内存泄漏和异常处理GG。
问题 纯虚函数的作用和实现方式
(1)作用:定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
应该在什么情况下使用纯虚函数?
1、当想在基类中抽象出一个方法,且该基类只做能被继承,而不能被实例化 纯虚类不能被实例化
2、这个方法必须在派生类(derived class)中被实现。
(2.)实现方式:纯虚函数的声明,是在虚函数声明的结尾加 = 0
,没有函数体。在派生类没有重新定义虚函数之前是不能调用的。
Q:字节对齐的原则
- 从0位置开始存储;
- 变量存储的起始位置是该变量大小的整数倍;
- 结构体总的大小是其最大元素的整数倍,不足的后面要补齐;
- 结构体中包含结构体,从结构体中最大元素的整数倍开始存;
- 如果加入pragma pack(n) ,取n和变量自身大小较小的一个。
Q:空结构体的sizeof()返回值
- 答案是1
Q:静态连接与动态链接的区别
- 静态链接
所谓静态链接就是在编译链接时直接将需要的执行代码拷贝到调用处,优点就是在程序发布的时候就不需要依赖库,也就是不再需要带着库一块发布,程序可以独立执行,但是体积可能会相对大一些。 - 动态链接
所谓动态链接就是在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝,缺点是由于是运行时加载,可能会影响程序的前期执行性能。