武汉卓越科技,面试问的八股文
1、32位系统中,一个指针占4
个字节?
2、vector在多线程环境是否安全?答案是安全
的。多线程使用–vector是线程安全的,方法同步,使用多线程时效率不高。
3、vector的resize和reverse有什么区别?
std::vector::reverse(size_type n);
要求vector容器的容量至少能够满足包含n个元素。
如果n大于vector当前的容量,这个方法会使容器重新分配存储提升容量到n或者是大于n。
如果n不大当前的容量,函数调用不会引起重新分配内存,并且容量也不受影响。
这个函数不会影响到容器的size和不能改变容器的元素。
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int>::size_type sz;
vecctor<int> foo;
sz = foo.capacity();
cout << "making foo grow:\n";
for(int i = 0; i < 100; i++){
foo.push_bacck(i);
/*
首先,在定义容器的时候,容器的容量为0
i = 0 capacity changed: 1
在push_back(0)之后,capacity变为1,与原来的不一样了,所以输出capacity changed: 1
i = 1 capacity changed: 2
在push_back(1)之后,capacity变为2,与原来的不一样了,所以输出capacity changed: 2
i = 2 capacity changed: 4
在push_back(2)之后,capacity变为4,与原来的不一样了,所以输出capacity changed: 4
在push_back(3)之后,capacity仍为4
i = 4 capacity changed: 8
在push_back(4)之后,capacity变为8,与原来的不一样了,所以输出capacity changed: 8
在push_back(5)之后,capacity仍为8
在push_back(6)之后,capacity仍为8
在push_back(7)之后,capacity仍为8
i = 8 capacity changed: 16
在push_back(8)之后,capacity变为16,与原来的不一样了,所以输出capacity changed: 16
在push_back(9)之后,capacity仍为16
...
i = 16 capacity changed: 32
在push_back(16)之后,capacity变为32,与原来的不一样了,所以输出capacity changed: 32
...
i = 32 capacity changed: 64
在push_back(32)之后,capacity变为64,与原来的不一样了,所以输出capacity changed: 64
i = 64 capacity changed: 128
在push_back(64)之后,capacity变为128,与原来的不一样了,所以输出capacity changed: 128
*/
if(sz != foo.capacity()){
sz = foo.capacity();
cout << "capacity changed: " << sz << endl;
}
}
}
根据官网给出的例子好像并没有点明reverse和resize的差别。
不过我们从上述中可以看出,当容量不足时,会自动的扩容。
参考其他两篇博客:
vector中resize()和reserve()区别
C++ vector中resize()和reserve()区别
说明:
首先是resize的用法:
_CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize) {
// trim or append value-initialized elements, provide strong guarantee
_Resize(_Newsize, _Value_init_tag{});
}
_CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Ty& _Val) {
// trim or append copies of _Val, provide strong guarantee
_Resize(_Newsize, _Val);
}
简单就是resize(int newsize)
和resize(int newsize, type value)
。
前者是将size重新调整为newsize。如果newsize小于oldsize,多出来的size会被删除。如果newsize大于oldsize,分配新的内存并初始化为默认值。
resize(int newsize, type value)
和resize(int newsize)
的区别就在于新分配的size用value去初始化。
reserve()
与容器的capacity
密切相关。源码是:
_CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) {
// increase capacity to _Newcapacity (without geometric growth), provide strong guarantee
if (_Newcapacity > capacity()) { // something to do (reserve() never shrinks)
if (_Newcapacity > max_size()) {
_Xlength();
}
_Reallocate_exactly(_Newcapacity);
}
}
reserve()
是调整capacity
的。如果newcapacity
大于oldcapacity
,则调整为newcapacity
;否则不调整capacity
。
区别:
(1)reserve()
避免多次不必要的扩容;
(2)resize()
改变容器的大小,且创建对象;
(3)reserve()
改变capacity
,但是不会改变size()
;resize()
不仅会修改size()
,也会修改capacity
;
(4)由于resize()
创建了对象,所有空间都会被初始化,所以可以直接访问;而reserve()
没有创建对象,所以新分配的空间不可直接访问;
(5)resize()
一定会引起变化,reverse()
不一定会引起变化。
4、C++11中的auto
auto是C++11新增的关键字。什么叫关键字?就是想int,float,double类似的类型。
通过auto进行类型自动推导,简化编程工作。
auto实际上是在编译时进行了类型推导,所有对程序的运行效率影响不大。
auto的限制:
(1)auto必须是个表达式,即auto必须与等号在一起时才能进行类型的推导;
(2)auto不能作为函数的参数;
(3)在类或结构体中,auto不能用于给静态成员变量。这又扯到编译相关的内容了;
(4)auto不能用于数组;
(5)auto不能用于推导模板参数。
auto a;//error
5、for_each中每个变量是否可更改?
是可以更改的。for_each是为每一个传入的变量作为某个函数的参数,这个函数是可以对变量进行更改的。
6、string如何修改长度?
string s;
s.resize();
void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem())
和reserve差不多,超过的就用默认值初始化,不超过的就截断。
7、给定一个点的坐标,如何判断点是在线的左边还是右边?
(1)如果线是垂直于x轴的,直接判断两者的x坐标;如果线是水平的,判断y;
(2)如果线是倾斜的,那么就把x代入会得到y,通过线上的y和点的y进行比较就可以了。
8、单例模式和工厂模式
程杰的《大话设计模式》上有,讲得很通俗易懂。
从9中的补充:
单例模式有3个要点:
(1)某个类只能有一个实例
(2)它必须自行创建这个实例
public class GirlFriend{
private static GirlFriend girlFriend;
//private volatile static GirlFriend girlFriend;
private String name;
private GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public static GirlFriend getInstance(String name){
if(girlFriend == null){
girlFriend = new GirlFriend(name);//它必须自行建立这个实例
//它必须自行向整个系统提供这个实例
}
return girlFriend;
}
}
(3)它必须自行向整个系统提供这个实例
9、单例模式中有个懒汉模式和饿汉模式?
曾看过一篇比较有趣的文章:单例模式–我的机器人女友
java写的,其中volatile
和synchronized
不太理解。
public class GirlFriend{
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public void smile(){
System.out.println("smile");
}
public static void main(String[] args{
GirlFriend girlFriend = new GirlFriend("小丽");
girlFriend.smile();
}
}
//单例模式
public class GirlFriend{
public static GirlFriend girlFriend;
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public static GirlFriend getInstance(String name){
if(girlFriend == null){
girlFriend = new GirlFriend(name);
}
}
public void smile(){
System.out.println("smile");
}
public static void main(String[] args{
//GirlFriend girlFriend = new GirlFriend("小丽");
GirlFriend girlFriend = GirlFriend.getInstance("小丽");
girlFriend.smile();
}
}
//通过多线程破坏单例模式
//单例模式
public class GirlFriend{
public static GirlFriend girlFriend;
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public static GirlFriend getInstance(String name){
if(girlFriend == null){
girlFriend = new GirlFriend(name);
}
}
public void smile(){
System.out.println("smile");
}
public static void main(String[] args){
//GirlFriend girlFriend = new GirlFriend("小丽");
for(int i = 0; i < 5; i++){//5个线程同时运行,顺利创建多个不同的对象。
new thread( new Runable(){
@Override
public void run(){
GirlFriend girlFriend = GirlFriend.getInstance("小丽");
girlFriend.smile();
}
}).start();
/***********************************************
线程1 线程2
T1: if(girlFriend == null){
T2: if(girlFriend == null){
T3: girlFriend = new GirlFriend(name);
T4: girlFriend = new GirlFriend(name);
T5: return girlFriend;
T6: return girlFriend;
线程1和线程2判断girlFriend的时候如果都为空,就会各自创建一个对象,最后就会返回两个不同的对象了。
***********************************************/
}
}
}
//懒汉式:添加同步锁
public class GirlFriend{
public static GirlFriend girlFriend;
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
//既然多线程会破坏单例模式,那么使用进程互斥就可以阻止多线程的不安全问题了。
//使用互斥锁synchronized
public synchronized static GirlFriend getInstance(String name){
if(girlFriend == null){
girlFriend = new GirlFriend(name);
}
return girlFriend;
}
/*
使用互斥锁会产生一些效率问题。
synchronized同步方法只有第一次创建对象的时候用得到,一旦创建了girlFriend对象后
就用不到这个同步功能了,以后每次调用getInstance方法都会同步代码,严重降低了效率。
*/
public void smile(){
System.out.println("smile");
}
public static void main(String[] args){
GirlFriend girlFriend = GirlFriend.getInstance("小丽");
girlFriend.smile();
}
}
//饿汉式:在类加载的时候就创建对象。
public class GirlFriend{
public static GirlFriend girlFriend = new GirlFriend("小丽);
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public static GirlFriend getInstance(String name){
return girlFriend;
}
/*
存在的问题:
(1)不支持延迟加载(在真正用到对象的时候,再创建实例);在类加载的时候,对象就创建好了。
如果对象在整个过程中一次都用不到,提前创建就浪费了。
(2)不能控制对象的数量,完全可以声明多个对象,比如
GirlFriend girlFriend1;
GirlFriend girlFriend2;
GirlFriend girlFriend3;
(3)可能没有足够的信息在静态初始化时,实例化每个对象,对象的构造方法参数可能要依赖程序后面的运算结果。
*/
public void smile(){
System.out.println("smile");
}
public static void main(String[] args){
GirlFriend girlFriend = GirlFriend.getInstance("小丽");
girlFriend.smile();
}
}
//双重检测
public class GirlFriend{
//volatile关键字保证了每个线程看到的girlFriend对象都是最新的。
public volatile static GirlFriend girlFriend;
private String name;
public GirlFriend(String name){
this.name = name;
System.out.println("制作完成");
}
public static GirlFriend getInstance(String name){
if(girlFriend == null){//检查girlFriend对象的时候,如果为null就进入同步代码
synchronized (GirlFriend.class){
if(girlFriend == null){//每个线程重新判断 volatile girlFriend对象是否为空,
girlFriend = new GirlFriend(name);
}
}
}
return girlFriend;
}
public void smile(){
System.out.println("smile");
}
public static void main(String[] args){
GirlFriend girlFriend = GirlFriend.getInstance("小丽");
girlFriend.smile();
}
}
10、SQL server数据库,找出前20个id,id不连号。
现在放弃。
11、链表实现栈操作:
数据结构:栈 ADT :使用链表实现对栈的基本操作。
首先链表有一个头阶段,除了头节点之外,就是元素所在的位置。
每一次入栈时,都是在头节点的下一个位置处插入新的节点;
每一次出栈时,都是在头节点的下一个位置处free一个节点。
12、线程间互斥方式
信号量和互斥锁
13、线程间通信方式?
线程互斥:互斥锁,临界资源,一次只能一个线程访问。
线程同步(生产者/消费者问题)
线程通信:线程共享同一进程的内存地址空间,通过全局变量实现数据交换。