现代C++(常量、变量及其初始化、控制流、模板)


Modern C++

第二章

常量

nullptr
  1. 为了取代NULL,因为传统C++不区分NULL和0,会导致在重载性质时发生混乱

    //C++不允许void * 隐式转换到其他类型
    foo(NULL) 会调用foo(int)
    
  2. 举例

    void foo(char *);
    if (std::is_same<decltype(NULL), decltype(0)>::value)//只有这个被调用
    	std::cout << "NULL == 0" << std::endl;
    if (std::is_same<decltype(NULL), decltype((void*)0)>::value)
    	std::cout << "NULL == (void *)0" << std::endl;
    if (std::is_same<decltype(NULL), std::nullptr_t>::value)
    	std::cout << "NULL == nullptr" << std::endl;
    	foo(nullptr);//会调用foo(char *) 
    

    养成使用nullptr的习惯

constexpr
  1. 常量表达式:允许一些计算只在编译时进行一次,而不是每次程序运行时;

    1. 例如:字面值常量123,‘a’,3.14
    2. 字面值常量相关的一些表达式:123+3.14,2<<2
  2. 存在的原因:编译器需要在,编译时就能把这些表达式,直接指出到程序中,例如数组大小的定义

    #include <iostream>
    #define LEN 10
    int len_foo() 
    {
        int i = 2;
        return i;
    }
    constexpr int len_foo_constexpr() 
    {
        return 5; 
    }
    constexpr int fibonacci(const int n) 
    {
    	return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
    }
    int main() 
    {
        char arr_1[10]; // 合法
        char arr_2[LEN]; // 合法
        int len = 10;
        // char arr_3[len]; // 非法
        const int len_2 = len + 1;
        constexpr int len_2_constexpr = 1 + 2 + 3;
    	// char arr_4[len_2]; // 非法
    	char arr_4[len_2_constexpr]; // 合法
    	// char arr_5[len_foo()+5]; // 非法
    	char arr_6[len_foo_constexpr() + 1]; // 合法
    	std::cout << fibonacci(10) << std::endl;
    	// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
    	std::cout << fibonacci(10) << std::endl;
    	return 0; 
    }
    
  3. 由于编译器的优化,可能会使得,非法行为变得合法。

  4. constexpr会进行验证检查。

变量及其初始化

if/switch变量声明强化
  1. 传统C++能在for语句中声明一个临时变量int,但没办法在if/switch声明一个临时变量

    C++17
    if (const std::vector<int>::iterator itr = std::find(vec.begin(),vec.end(), 3);itr != vec.end()) 
    {
    *itr = 4; 
    }
    
列表初始化 std::initializer_list
  1. 传统C++ ,没有构造函数、析构和虚函数都可以通过{}进行初始化,类对象的初始化需要通过拷贝构造需要使用()

  2. C++11引入std::initializer_list允许构造函数和其他函数一样使用初始化列表:这种构造函数被叫做:初始化列表构造函数

    class MagicFoo {
    public:
        std::vector<int> vec;
        MagicFoo(std::initializer_list<int> list) {
            for (std::initializer_list<int>::iterator it = list.begin();
            it != list.end(); ++it)
            vec.push_back(*it);
    	}
    };
    	MagicFoo magicFoo = {1, 2, 3, 4, 5};
    
    
  3. 还可以做为普通函数的形参

    void foo(std::initializer_list<int> list) {
        for (std::initializer_list<int>::iterator it = list.begin();
        it != list.end(); ++it) vec.push_back(*it);
    }
    	foo({6,7,8,9});
    

    统一的语法来初始化任一对象Foo foo2 {3, 4};

结构化绑定 std::tuple< int , double ,std:: string >
  1. C++11新增 std:: tuple容器用于构造一个元组,返回多个值。

    std::tuple<int, double, std::string> f() {
    	return std::make_tuple(1, 2.3, "456");
    }
    //C++17 完善了C++11/14 拆包的操作
    
    int main() {
        auto [x, y, z] = f();// 牛逼
        std::cout << x << ", " << y << ", " << z << std::endl;
        return 0; 
    }
    
类型推导 auto和decltype
auto
  1. 自动推导参数类型,甚至是函数的形参,但是目前还不能推导数组类型
class MagicFoo {
public:
	std::vector<int> vec;
	MagicFoo(std::initializer_list<int> list) {
// 从 C++11 起, 使用 auto 关键字进行类型推导
        for (auto it = list.begin(); it != list.end(); ++it) {
        	vec.push_back(*it);
		} 
	}
};
int add(auto x, auto y) {
	return x+y;
}
auto i = 5; // 被推导为 int
auto j = 6; // 被推导为 int
std::cout << add(i, j) << std::endl;
auto auto_arr2[10] = {arr}; // 错误, 无法推导数组元素类型
decltype
  1. 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的,用来推导某一个表达式的类型

    if(std:is_same< decltype(x), float>::value)
    {
    	std::cout<<"type x = float"<<std::endl;
    }
    
std : : is_same < T,U >
  1. 用于判断T 和 U 这两个类型是否相等
尾返回类型推导
template<typename T, typename U>
auto add3(T x, U y){
    return x + y;
}
//after C++11
auto w = add2<int, double>(1, 2.0);
decltype(auto)
  1. decltype(auto) 主要用于对转发函数或封装的返回类型进行推导,它使我们无需显式的

指定 decltype 的参数表达式。

std::string lookup1();
std::string& lookup2();
//在 C++11 中,封装实现是如下形式:
std::string look_up_a_string_1() {
	return lookup1();
}
std::string& look_up_a_string_2() {
	return lookup2();
}
//而有了 decltype(auto),我们可以让编译器完成这一件烦人的参数转发:
decltype(auto) look_up_a_string_1() { // auto 用于自动推导lookup1()的返回值类型。
	return lookup1();
}
decltype(auto) look_up_a_string_2() {
	return lookup2();
}

控制流

if constexpr
  1. C++11引入constexpt关键字,C++17将constexpr 引入到if语句中,用来简化代码,例如:

    #include <iostream>
    template<typename T>
    auto print_type_info(const T& t) {
        if constexpr (std::is_integral<T>::value) { //当没有constexpr会导致 auto推导错误,既有int 又有double类型
        											//加入constexpr能够避免错误。
        	return t + 1; 
        } 
        else {
        	return t + 0.001; 
        } 
    }
    int main() {
        std::cout << print_type_info(5) << std::endl;// 6
        std::cout << print_type_info(3.14) << std::endl;//3.141
    }
    
区间for 迭代
  1. 简单for循环遍历

    for(auto element :vec ) // 只读
    
    for(auto &element : vec) // 可写
    

模板

外部模板
  1. 外部模板需要我们显示的告诉编译文件才能进行实例化
template class std :vector<bool>; 
extern template class std:: vector<double > 不在当前编译文件中实例化模板
尖括号 “>”
  1. 传统>> 一律被当成右移运算符, C++ 11 开始连续的右尖括号变得合法
类型别名模板
  1. C++11使用using 引入下面的写法,typedef定义别名的规则 : typedef 原名称 新名称

  2. typedef 定义函数指针

    typedef int (* fun)(int, int);
    int add(int x, int y){
    	return x + y ;
    }
    func f = add;
    
  3. using 定义函数指针

    using P = int(*)(int, int);*//等价上一条*
    int add(int x, int y){
    	return x + y ;
    }
    p = a 
    
变长参数模板
  1. C++11之前只能接受固定数量的模板参数,C++11加入了新的表示方式,允许任意个数,任意类型的模板参数

    template<typename ... Ts> class Magic 
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值