面试之快速学习C++14

文章参考:https://zhuanlan.zhihu.com/p/588826142?utm_id=0

最近学了一会感慨到找工作好难,上周面试了一家医疗公司,准备攒攒经验但是不去,结果三天了没消息,感觉一面都没过...

本来自傲看不上,结果人家也看不上你...没事攒攒经验,面试官问了是否会C++14我说我不会,问题不大,现在看看来得及~

1. 变量模版

c 复制代码
/*
 1. 变量模版 ------------
 */
//1.引入的意义,以前只有模版类和模版函数,比如我想定义一个各种类型的常量,那么不能用模版,C++14可以了
template <typename T>
constexpr T pi = T(3.1415926);

//类内模版变量的定义
struct VariableTemplate1 {
    template<typename T>
    static T min;
};
//注意这里要加一个T ,min会使用默认值
template<typename T>
T VariableTemplate1::min = {};

//但是你可以偏特化
template<>
const float VariableTemplate1::min<float> = 6.666;
template<>
const std::string VariableTemplate1::min<std::string> = "hhh";
//我可以理解为,变成了三个变量?
//变量模版和类型推导
template <typename T>
constexpr T pi1 = T{3.1415926};

void testVariableTemplate() {
    
    //pi是一个double
    double diameter = 2 * pi<double> * 3;
    std::cout << "diameter<double> = " << diameter <<std::endl;
    //pi是一个int
    diameter = 2 * pi<int> * 3;
    std::cout << "diameter<int> = " << diameter <<std::endl;

    VariableTemplate1::min<int> = 3;
    std::cout << "VariableTemplate1::min = " << VariableTemplate1::min<int> <<std::endl;
    
//    VariableTemplate1::min<std::string> = "hhh"; //会报错,因为int模版对应了默认的模版值,现在不知道对应哪个模版
    std::cout << "VariableTemplate1::min = " << VariableTemplate1::min<std::string> <<std::endl;
    std::cout << "VariableTemplate1::min = " << VariableTemplate1::min<double> <<std::endl;
    std::cout << "VariableTemplate1::min = " << VariableTemplate1::min<float> <<std::endl;
    //0  ???是不是因为第一次确定类型之后就不会改了
    
    auto pi3 = pi<int>;
    auto pi4 = pi<double>;
    auto pi5 = pi<char>;
    
    std::cout << "pi3 = " << pi3 <<std::endl;
    std::cout << "pi4 = " << pi4 <<std::endl;
    std::cout << "pi5 = " << pi5 <<std::endl;
    /*
     diameter<double> = 18.8496
     diameter<int> = 18
     VariableTemplate1::min = 3
     VariableTemplate1::min = hhh
     VariableTemplate1::min = 0
     VariableTemplate1::min = 6.666
     pi3 = 3
     pi4 = 3.14159
     pi5 = 
     */
    
}

问题

  1. 为什么一个min可以对应这么多值?所以实际上它是会根据实例化的值生成一个单独的值?

感受

1 . 它可以与auto配合使用去做类型转换,生成不同的常量。

2 . 可以用于在一个类里面定义很多不同类型的静态常量

2.lambda

c 复制代码
/*
 2. lambda表达式的改动 ------------
 */
//1. 支持lamda泛型参数
//2. 支持初始化捕获

void testLambda() {
    int x = 7;
    
    //捕获x并给它初始值,意义在哪里?
    auto func = [x = 3](auto y) {
        return x+y;
    };
    
    //泛型参数这个比较好理解,初始化捕获有什么好处呢。在c++11中,lambda表示捕获变量只能通过值捕获或者引用捕获,支持了初始化表达式之后,我们就可以更灵活的捕获了,比如移动捕获
    std::string str = "xixixixi";
    std::string str1;
    auto func1 = [str1 = std::move(str)]{
        return str1 + ", hhhhhh";
    };
    std::cout << "func(3)" << func(3) << std::endl;
    std::cout << "func(4.5)" << func(4.5) << std::endl;
    
    std::cout << "func1()" << func1() << std::endl;
    /*
     func(3)6
     func(4.5)7.5
     func1()xixixixi, hhhhhh
     */
}
//如果有学习c++11的function、bind以及lambda表达式,你应该会知道bind相对于lambda表达式的两个最重要的优势就是泛型参数和移动捕获,从c++14之后,bind的这个两个优势就完全不复存在了,并且lambda表达式对象会比bind生成的对象更小,所以基本上可以让bind光荣的下岗了(但是实际bind到了c++20以后也依旧存在,哪可能有它存在的意义把)。

3. constexpr限制放宽

c 复制代码
/*
3. constexpr限制放宽------

c++11中

constexpr修饰变量,要求变量必须是可以在编译器推导出来;
constexpr修饰函数(其实就是修饰函数返回值),除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句;
constexpr同时可以修饰构造函数,但是也会要求使用这个构造函数时,可以在编译器就把相关的内容全部推导出来
c++14中,对constexpr的限制放宽了,允许使用循环、if、switch等等语句,但是主旨还是一样的,需要在编译期间就可以计算出全部内容,限制放宽之后,这个关键字便可以更灵活的使用了。

*/
//比如在c++11中如果想用constexpr计算前n项和,那么需要像下面这样写

constexpr int testConexpresFunc1(int n) {
   return n > 0 ? testConexpresFunc1(n - 1) +  n : 0;
}

//在c++14中就可以使用if、局部变量和循环了(c++14可以,c++11报错)
constexpr int testConexpresFunc2(int n) {
   if (n <= 0){
          
       return 0;
   }
   int sum = 0;
   for (int i = 1; i <= n; ++i) {
       sum += i;
   }
   return sum;
   
   /*
    testConexpresFunc1(5) = 15
    testConexpresFunc2(5) = 15
    */
}

4. 数字分隔符

c 复制代码
    // 三个数字的结果完全一样
    int val1 = 100000000;
    int val2 = 100'000'000;
    int val2 = 100'00'0'0'0'0;

5. 函数返回值推导

c 复制代码
/*
 5. 函数返回值推导 ------------
 */

//回顾c++11返回值类型后置,一定要auto + decltype
template<typename T, typename U>
auto add1(T a, U b)->decltype(a+b) {
    return a+b;
}

//c++14之后相当于不需要decltype了
template<typename T, typename U>
auto add2(T a, U b) {
    return a+b;
}

//限制条件如下:
//1. 如果有多个推导语句,那么推导的结果必须保持一致,实际上编译器就是根据函数里面的return推导的吧!
// 编译报错,第一个return推导为int,第二个return推导为double,两次推导结果不一致
//报错: 'auto' in return type deduced as 'double' here but deduced as 'int' in earlier return statement
//auto add3 (int flag)
//{
//    if (flag < 0)
//    {
//        return 1 ;
//    }
//    else
//    {
//        return 3.14;
//    }
//}

//2.如果没有return或者return为void类型,那么auto会被推导为void。
//3. 一旦在函数中看到return语句,从该语句推导出的返回类型就可以在函数的其余部分中使用,包括在其他return语句中,即,优先推导论,优先推导是什么类型就是类型。
auto add4(int i)
{
    if (i <= 1)
    {
        return i; // 返回值被推导为int
    }
    else
    {
        return add4(i - 1) + i; // sum的返回值已经被推导出来了,所以这里是没有问题的
    }
}
//报错:Function 'add5' with deduced return type cannot be used before it is defined
//auto add5(int i)
//{
//    if (i > 1)
//    {
//        return add5(i - 1) + i;
//    }
//    else
//    {
//        return i;
//    }
//}
//4. 不能推导初始化列表。
//报错:Cannot deduce return type from initializer list
//auto add6(int i)
//{
//    return {1,2,3,4};
//}
//5. 虚函数不能使用返回值推导

void testTrailingReturnType() {
    std::cout << "add1(5,2) = " << add1(5,2) << std::endl;
    std::cout << "add2(5,2) = " << add2(5,2) << std::endl;
    std::cout << "add4(5,2) = " << add4(5) << std::endl;
    /*
     add1(5,2) = 7
     add2(5,2) = 7
     add4(5,2) = 15
     */
}

6. [[deprecated]]标记

美 [ˈdeprəkeɪtɪd]

vt. 强烈反对,抨击;对......表示不赞成;

c 复制代码
/*
 6. [[deprecated]]标记 ------------
 */
//在打算废弃一些接口的时候,可以直接打个标记过渡,然后在后续的版本就可以完全清理掉。
[[deprecated]]
void deprecatedFunc() {
    //do nothing
}

void testDeprecatedFunc() {
    //warning'deprecatedFunc' is deprecated
    deprecatedFunc();
}

7. 库的新特性

c 复制代码
/*
 库的新特性
 */
/*
 7. 新增std::make_unique
 */
class A {
public:
    A(const int a):a_(a){}
    int getA(){return a_;}
private:
    int a_;
};
void testMakeUnique() {
    //新增std::make_unique
    std::unique_ptr<A> pt = std::make_unique<A>(1);
    std::cout << "pt = " << pt->getA() << std::endl;
}

/*
 2.新增读写锁std::shared_timed_mutex与std::shared_lock
 */
//c++11引入了多线程线程的一些库,但是是没有读写锁的,因此在c++14引入了读写锁的相关实现(头文件shared_mutex),其实c++14读写锁也还不够完善,知道c++17读写锁这块才算是完备起来,后面写c++17的时候计划把这块再完整的梳理一下。
/* 当Mutex是shared_mutex 的时候

1.当有函数使用共享锁时,能够与其他享有共享锁的函数也能并发同步执行。而和所有独占锁的函数是互斥的。

2.当有一个函数使用独占锁时,其他所有的用同一个Mutex带锁的函数都是与其互斥的。
*/
//我理解是这个std::shared_mutex实际上是互斥量,是否是共享锁取决于用std::shared_lock<std::shared_mutex> ,还是std::unique_lock<std::shared_mutex> 还是std::lock_guard<std::shared_mutex>

std::shared_mutex g_sMutex;
std::shared_timed_mutex g_shared_m;
void writeTime(const std::string funName ,int time) {
    for (size_t i = 1; i < time; i++) {
        std::string outPut = funName + ":" + std::to_string(time);
        std::cout << outPut << std::endl;
        sleep(1);
    }
    
}
 
void shared_1(int seconds) {
    std::shared_lock<std::shared_timed_mutex> sl(g_shared_m);
    std::cout << "shared_1" << std::endl;
    writeTime("shared_1" , seconds);
}
 
void shared_2(int seconds) {
    std::shared_lock<std::shared_timed_mutex> theLock(g_shared_m);
    std::cout << "shared_2" << std::endl;
    writeTime("shared_2", seconds);
}

void testSharedMutext() {
    std::vector<std::thread> vecThrea;
    vecThrea.push_back(std::thread(shared_1,10));
    vecThrea.push_back(std::thread(shared_2,10));
    for (auto & oneThread : vecThrea)
    {
        oneThread.join();
    }
    /*
     shared_1
     shared_2
     shared_2:10
     shared_1:10
     shared_2:10
     shared_1:10
     shared_2:10
     shared_1:10
     shared_1:10
     shared_2:10
     shared_1:10
     shared_2:10
     shared_1:10
     shared_2:10
     shared_1:10
     shared_2:10
     shared_2:10
     shared_1:10
     shared_2:10
     shared_1:10
     */
}
/*
 3. 新增std::exchange
 */
//c++14新增了一个接口std::exchange(头文件utility),其实这个也并不算是新增的,因为这个接口其实在c++11的时候就有了,只不过在c++11中作为一个内部函数,不暴露给用户使用,在c++14中才把它暴露出来给用户使用。使用方法也很简单。
//exchange会把第二个值赋值给第一个值,但是不会改变第二个值。我们来看下它的实现吧。


//自己仿写一个, 除此之外,我们这里说明一个关键的点。exchange的第二个值是万能引用,所以说他是既可以接收左值,也可以接收右值的,所以我们可以这样来使用。
template <typename T, typename U>
T myExchange(T &a, U &&b) {
    T old_value = std::move(a);
    a = std::forward<U>(b);
    return old_value;
}

void testExchange() {
    std::string s1 = "hello";
    std::string s2 = "world";
    std::cout << s1 << " " << s2 << std::endl;
    std::exchange(s1, s2);
    std::cout << s1 << " " << s2 << std::endl;
    /*
     hello  world
     world world
     */
    myExchange(s1, s2);
    std::cout << s1 << " " << s2 << std::endl;
    myExchange(s1, std::move(s2));
    std::cout << s1 << "|  " << s2 << std::endl;
    /*
     hello world
     world world
     world world
     world|
     */

}

/*
 4. 新增std::quoted
 英  [kwəʊtid]   美  [ˈkwoʊtɪd]
v. 引证(quote 的过去式)
 */
//C++14引入std::quoted用于给字符串添加双引号

void testQuoted() {
    std::string str = "Hello world!";
    std::cout << str << std::endl;
    std::cout << std::quoted(str) << std::endl;
    /*
     Hello world!
     "Hello world!"
     */
}
相关推荐
GISDance13 分钟前
2025年高考志愿填报指导资料
学习·考研·高考
呃m14 分钟前
双重特征c++
c++
景彡先生32 分钟前
C++ 中文件 IO 操作详解
开发语言·c++
weixin_464078071 小时前
Python学习小结
python·学习
无影无踪的青蛙1 小时前
[C++] STL大家族之<map>(字典)容器(附洛谷)
开发语言·c++
二进制人工智能1 小时前
【OpenGL学习】(四)统一着色和插值着色
c++·opengl
GISer_Jing2 小时前
Axios面试常见问题详解
前端·javascript·面试
红石程序员3 小时前
VSCode配置C++项目全攻略
开发语言·c++·visual studio
求职小程序华东同舟求职3 小时前
互联网校招腾讯26届校招暑期实习综合素质测评答题攻略及真题题库
面试·职场和发展·求职招聘·求职
测试19983 小时前
2025软件测试面试题汇总(接口测试篇)
自动化测试·软件测试·python·测试工具·面试·职场和发展·接口测试