武汉卓越科技面试复盘

武汉卓越科技,面试问的八股文
在这里插入图片描述

1、32位系统中,一个指针占4个字节?
2、vector在多线程环境是否安全?答案是安全的。多线程使用–vector是线程安全的,方法同步,使用多线程时效率不高。
3、vector的resize和reverse有什么区别?

C++引用网址

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写的,其中volatilesynchronized不太理解。

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、线程间通信方式?

线程互斥:互斥锁,临界资源,一次只能一个线程访问。
线程同步(生产者/消费者问题)
线程通信:线程共享同一进程的内存地址空间,通过全局变量实现数据交换。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值