商汤开发面经

322 阅读3分钟

上来直接问C++11新特性,我说了智能指针,然后让我手写shared_ptr,我说不太会。。然后让我手写string。。

C++

1. C++11新特性有哪些?

  1. auto & decltype 关键字 在编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然而要做到这一点并非那么容易,为了解决这个问题,C++11引入了auto类型说明符,它能让编译器替我们去分析表达式所属的类型。即auto让编译器通过初始值来推算变量的类型,所以auto定义的变量必须有初值
// 由va1 和 va2 相加的结果可以推断出item的类型
auto item = va1 + va2;  // item初始化为va1和va2相加的结果

有时候还会遇到这种情况:希望从表达式的类型推断出要定义的变量的类型,但是不想用该表达式的值初始化变量。为此,C++11 引入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值

decltype(f()) sum = x; // sum的类型就是函数f的返回类型

编译器并不实际调用函数f,而是使用当调用发生时f的返回值类型作为sum的类型
decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)

const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x的类型是const int
decltype(cj) y = x; // y的类型是const int&,y绑定到变量x
decltype(cj) z; // 错误!z是一个引用,必须初始化
  1. 右值引用、move函数、完美转发:可以见juejin.cn/post/703394…

  2. 新特性还有智能指针、范围for循环、lambda表达式等等。。。

2. 聊聊智能指针

juejin.cn/post/703399…

3. 实现shared_ptr


// 暂时未考虑线程安全问题
template <typename T> class shared_ptr {
    public:
        shared_ptr(T* p) : count(new int(1)), ptr(p) {} // 构造函数
        shared_ptr(const shared_ptr<T>& p) : count(p.count), ptr(p.ptr) { // 拷贝构造
            (*count)++;
        } 
        shared_ptr<T>& operator=(const shared_ptr<T>& p); // 拷贝赋值运算符
        T* operator->() { return ptr; }
        T& operator*() {return *ptr; }
        ~shared_ptr() {
            if(--(*count) == 0) {
                delete count;
                delete ptr;
            }
        } 
    private:
        int* count; // 实现计数机制
        T* ptr;
};


template <typename T> shared_ptr<T>& shared_ptr<T>::operator=(const shared_ptr<T>& p) {
    if(this != &p) { // 注意这里的写法,防止自我赋值
        if(--(*count) == 0) {
            delete ptr;
            delete count;
        }
        ptr = p.ptr;
        count = p.count;
        ++(*count);
    }
    return *this;
}

4. 实现string

#include<cstring>

class string {
    public:
        string(const char* ch = nullptr);
        string(const string& s); // 拷贝构造函数(需要进行深复制)
        string& operator=(const string& s); // 拷贝赋值运算符
        string operator+(const string& s);
        string& operator+=(const string& s);
        int size() { return len; }
        ~string() { 
            delete[] str;
        }
        
    private:
        char* str;
        int len;  // 字符串长度
};

string::string(const char* ch) {
    if(!ch) {
        len = 0;
        str = new char[1];
        str[0] = '\0';
    }
    else {
        len = strlen(ch); // 传入strlen的指针必须指向以 空字符 作为结束的数组,但是它记录的长度不包括空字符
        str = new char[len + 1];
        strcpy(str, ch);
    }
}

string::string(const string& s) {
    len = s.len;
    str = new char[len + 1];
    strcpy(str, s.str);
}

string& string::operator=(const string& s) {
    if(this != &s) {
        delete[] str;
        len = s.len;
        str = new char[len + 1];
        strcpy(str, s.str);
    }
    return *this;
}

string string::operator+(const string& s) {
    string newstring;
    newstring.len = len + s.len;
    newstring.str = new char[newstring.len + 1];
    strcpy(newstring.str, str);
    strcat(newstring.str, s.str);
    return newstring;
}

string& string::operator+=(const string& s) {
    len += s.len;
    char* newstr = new char[len + 1];
    strcpy(newstr, str);
    strcat(newstr, s.str);
    delete[] str;
    str = newstr;
    return *this;
}

操作系统

1. 怎么处理多线程冲突?

当多个线程或进程同时处理一个文件/数据而发生冲突时,可以通过加锁或解锁来解决,或者规定它们的优先级