c++面试宝典

本文概述了C++中多线程的mutex、condition_variable及其实现,智能指针如shared_ptr和unique_ptr的用法,以及内存管理、算法和设计模式等内容,适合面试准备。

目录

一 多线程

二 指针

三  字符串

四  面向对象

五 基本用法

六 c++11

七  算法


c++面试必考多线程,内存(智能指针),常见算法,设计模式。

一 多线程

c++11提供了mutex和condition_variable,并没有提供临界区,信号量。(线程同步)

Mutex互斥量,C++ 11中使用 std::mutex类,必须包含 <mutex> 头文件。

(1)Mutex系列类(四种)
std::mutex,最基本的 Mutex 类。构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。lock()还是try_lock()都需要和unlock()配套使用。但性能损耗,推荐使用atomic操作数据类型
std::recursive_mutex,递归 Mutex 类。
std::time_mutex,定时 Mutex 类。

std::recursive_timed_mutex,定时递归 Mutex 类。

(2)Lock系列类(两种)----------这两个类相比使用std::mutex的优势在于不用配对使用,无需担心忘记调用unlock而导致的程序死锁
std::lock_guard,方便线程对互斥量上锁。除了构造函数外没有其他成员函数
std::unique_lock,除了lock_guard的功能外,提供了更多的成员函数,相对来说更灵活一些。这些成员函数包括lock,try_lock,try_lock_for,try_lock_until、unlock等

(3) 条件变量   

std::condition_variable 提供了两种 wait() 函数

无条件等待:

void wait (unique_lock<mutex>& lck);

有条件等待

template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);

std::condition_variable::notify_one()   唤醒其中一个等待线程(不确定)

std::condition_variable::notify_all()   唤醒所有的等待(wait)线程。如果当前没有等待线程,则该函数什么也不做

(4)线程池实现原理

//ThreadPool.h

class ThreadPool{
public:
    //自定义void()的函数类型
    typedef function<void()>Task;
    ThreadPool();
    ~ThreadPool();
public:
    size_t initnum;
    //线程数组
    vector<thread>threads ;
    //任务队列
    queue<Task>task ;
    
    //互斥锁条件变量
    mutex _mutex ;
    condition_variable cond ;
    
    //线程池工作结束时为真
    bool done ;
    
    //队列是否为空
    bool isEmpty ;
    //队列是否为满
    bool isFull;

public:
    void addTask(const Task&f);
    void start(int num);
    void setSize(int num);
    void runTask();
    void finish();
};

//ThreadPool.cpp
#include"ThreadPool.h"
ThreadPool ::ThreadPool():done(false),isEmpty(true),isFull(false){
}

//设置池中初始线程数
void ThreadPool::setSize(int num){
        (*this).initnum = num ;
}

//添加任务
void ThreadPool::addTask(const Task&f)
{
    if(!done){
        //保护共享资源    
        unique_lock<mutex>lk(_mutex);
        
        //要是任务数量到了最大,就等待处理完再添加
        while(isFull){
            cond.wait(lk);
        }

        //给队列中添加任务
        task.push(f);
        
        if(task.size()==initnum)
            isFull = true;
        
        cout<<"Add a task"<<endl;
        isEmpty = false ;
        cond.notify_one();
    }
}

void ThreadPool::finish()
{
        //线程池结束工作
        for(size_t i =0 ;i<threads.size();i++){
               threads[i].join() ;
        }
}

void ThreadPool::runTask(){
    
    //不断遍历队列,判断要是有任务的话,就执行
    while(!done){

        unique_lock<mutex>lk(_mutex);
        
        //队列为空的话,就等待任务
        while(isEmpty){
            cond.wait(lk);
        }
        
        Task ta ;
        //转移控制快,将左值引用转换为右值引用
        ta = move(task.front());  
        task.pop();
        
        if(task.empty()){
            isEmpty = true ;    
        }    
        
        isFull =false ;
        ta();
        cond.notify_one();
    }
}

void ThreadPool::start(int num){
    
    setSize(num);
    
    for(int i=0;i<num;i++){        
        threads.push_back(thread(&ThreadPool::runTask,this));
    }
}
ThreadPool::~ThreadPool(){   
}

//Test.cpp

#include <iostream>
#include"ThreadPool.h"
void func(int i){
    cout<<"task finish"<<"------>"<<i<<endl;
}
int main()
{

    ThreadPool p ;
    p.start(N);
    int i=0;

    while(1){
        i++;
        //调整线程之间cpu调度,可以去掉
       this_thread::sleep_for(chrono::seconds(1));
        auto task = bind(func,i);
        p.addTask(task);
    }

    p.finish();
    return 0;
}
 

二 指针

0 智能指针

shared_ptr,本质是类模板,包含2个要素:1要管理的资源,2引用计数(atomic原子变量实现,线程安全)。

每次创建类的新对象时,初始化指针并将引用计数count=1,拷贝构造函数count++,赋值运算符重载左操作数所指对象的引用计数count--,右操作数所指对象的引用计算count++

实现原理:引入2个类,一个引用计数类Counter,一个指针类SmartPtr

注意事项:1 不能将原始指针直接赋值给shared_ptr,不要将同一个原始指针初始化不同的shared_ptr,不要循环引用,不要delete  get函数获取的原始指针,删除器的使用

weak_ptr,配合shared_ptr使用,观测权,无引用计数

与shared_ptr不同,unique_ptr没有定义类似make_shared的操作,因此只可以使用new来分配内存,并且由于unique_ptr不可拷贝和赋值,初始化unique_ptr必须使用直接初始化的方式。

  1. unique_ptr<int> up1(new int()); //okay,直接初始化

  2. unique_ptr<int> up2 = new int(); //error! 构造函数是explicit

  3. unique_ptr<int> up3(up1); //error! 不允许拷贝

  4. int *p = new int;
    unique_ptr<int> p1(p);//原始指针拷贝,编译不报错,不推荐使用
    unique_ptr<int> p2(p);

  5. 警惕智能指针作为参数。  const  reference是智能指针做参数的底线。

unique_ptr   同一时刻只能有一个unique_ptr指向给定对象。当unique_ptr被销毁时,它所指向的对象也会被销毁。因此不允许多个unique_ptr指向同一个对象,所以不允许拷贝与赋值

unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权

shread_ptrunique_ptr都可以持有数组,但有区别,如下:

shared_ptr默认是使用delete来释放管理的资源,delete只会调用第一个元素的析构函数。要使用shared_ptr来管理数组,就需要需要自定义删除器

unique_ptr重载了管理数组的版本, unique_ptr就会自动使用delete[]来释放资源

1 new int[12]生成一个大小为12 的int数组,而new int(12)是生成一个初始值为12的int变量。

对于基本类型数组来说,delete a和delete [ ] a效果是一样的,如果a是一个用户自己定义的结构类型数组,只能使用delete [ ] a。

2下面的代码会输出:

int main()
{    
    int a[4]={1,2,3,4};
    int *ptr=(int *)(&a+1);
    printf("%d",*(ptr-1));
}
解析:最终结果会输出4。考察数组和指针, 指针加一的能力由指针指向的类型决定。&a和a都指的是数组首元素的地址,不同的是,a就是a+0,*(a+0)也就是a[0],而&a+1相当于对a[]类型的数组类型的指针加一,此时指针加到数组的末尾。ptr接受之后,ptr指向最后一个元素后面的那个位置,而ptr的类型是int*,因此

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步基

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值