深入应用C++11 笔记 (二)

本文探讨了C++11中的初始化列表和基于范围的for循环特性。介绍了如何使用std::initializer_list进行灵活初始化,并展示了基于范围的for循环简化容器遍历的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

深入应用C++11 笔记 (二)

第一章 程序简洁之道

1.3 初始化列表
  1. 给类添加一个std::initializer_list构造函数,它将拥有任意长度初始化的能力。

    class Foo
    {
    public:
    Foo(std::initializer_list<int>) {}
    };
    Foo foo = { 1,2,3,4,5 };//OK

    使用initializer_list来接收{…},如何通过它来给自定义容器赋值呢?代码如下:

    class FooVector
    {
    std::vector<int> content_;
    public:
    FooVector(std::initializer_list<int> list)
    {
        for (auto it = list.begin(); it != list.end(); ++it)
        {
            content_.push_back(*it);
        }
    }
    };
    
    class FooMap
    {
    std::map<int, int> content_;
    using pirt_t = std::map<int, int>::value_type;
    public:
    FooMap(std::initializer_list<pirt_t> list)
    {
        for (auto it = list.begin(); it != list.end(); ++it)
        {
            content_.insert(*it);
        }
    }
    };
    
    FooVector foo_1 = { 1,2,3,4,5 };
    FooMap foo_2 = { { 1,2 },{ 3,4 },{ 5,6 } };

    std::initializer_list不仅可以用来对自定义类型做初始化,还可以用来传递同类型的数据集合:

    void func(std::initializer_list<int> l)
    {
     for(auto it=l.begin();it!=l.end();++it)
     {
       std::cout<<*it<<std::endl;
     }
    }
    int main(void)
    {
     func({});       \\一个空集合
     func({1,2,3}); \\传递{1,2,3}
      return 0;
    }
  2. std::initializer_list的一些细节

    • 它是一个轻量级的容器类型,内部定义了iterator等容器必须的概念
    • 对于std::initializer_list <T>而言,它可以接收任意长度的初始化列表,但要求元素必须是同种类型T(或可转换为T)
    • 它有三个成员接口:size(),begin(),end()
    • 它只能被整体初始化或赋值
    std::initializer_list<int> list={1,2,3};
    size_t n=list.size();//n==3

    对std::initializer_list的访问只能通过begin()和end()进行循环遍历,遍历时取得的迭代器是只读的。如果需要修改std::initializer_list中某一个元素的值,可以通过初始化列表的赋值对std::initializer_list做整体修改

    std::initializer_list<int> list;
    size_t n=list.size();//n==0
    list={1,2,3,4,5};
    n=list.size();//n==5
    list={3,1,2,4};
    n=list.size();//n==4

    std::initializer_list的内部并不负责保存初始化列表中元素的拷贝,仅仅存储了列表中元素的引用而已

  3. 初始化列表会防止类型被收窄

    类型收窄包括以下几种情况:

    1. 从一个浮点数隐式转换为一个整型数,如int i=2.2

    2. 从高精度浮点数隐式转换为低精度浮点数,如从long double隐式转换为double或float

    3. 从一个整型数隐式转换为一个浮点数,并且超出了浮点数的表示范围,如:float x=(unsigned long long)-1

    4. 从一个整型数隐式转换为一个长度较短的整型数,并且超出了长度较短的整型数的表示范围,如char x=65536

      int a = 1.1;//OK
      int b = { 1.1 };//error
      
      float fa = 1.0e+40;//OK
      float fb = { 1.0e+40 };//error
      
      float fc = (unsigned long long) - 1;//OK
      float fd = { (unsigned long long) - 1 };//error
      float fe = (unsigned long long)1;//OK
      float ff = { (unsigned long long)1 };//OK
      
      const int x = 1024, y = 1;//OK
      char c = x;//OK
      char d = { x };//error
      char e = y;//OK
      char f = { y };//OK

      只有遇到类型变窄的情况,初始化列表就不回允许这种转换发生

    1.4 基于范围的for循环
    1. for循环的用法

      std::vector<int> arr;
      //<1> C++ 中遍历容器的一般方法
      for (auto it = arr.begin(); it != arr.end(); ++it)
      {
      std::cout << *it << std::endl;
      }
      void do_cout(int n)
      {
      std::cout << n << std::endl;
      }
      int main()
      {
      std::vector<int> arr = { 1,2,3,4,5,6,7,8 };
      //<2> stl 的algorithm中有一个for_each算法
      for_each(arr.begin(), arr.end(), do_cout);
        //或者使用lambda
          for_each(arr.begin(), arr.end(), [&](int n) {std::cout << n << std::endl; });
      }
      //C++11 中for用法
      for(auto n:arr)
      {
      std::cout<<n<<std::endl;
      }
      //当然 如果我命明确数据类型 可以直接写出
      for(int n:arr){};
      
      //这个例子是使用只读的方式遍历容器的,如果需要在遍历的时候修改,则需要使用引用
      for(auto &n:arr){};
    2. 基于范围的for循环使用细节

      //对map进行遍历
      std::map<std::string, int> mm = { {"1",1},{"2",2},{"3",3} };
      for (auto &val : mm)
      {
      std::cout << val.first << "->" << val.second << std::endl;
      }

      注意点:

      • for循环中val的类型是std::piar。因此,对map这种关联性容器而言,需要使用val.first或val.second来提取键值。
      • auto自动推导出的类型是容器中的value_type,而不是迭代器。
    3. 基于范围的for循环对容器的访问频率

      std::vector<int> arr={1,2,3,4};
      std::vector<int>& get_range(void)
      {
      std::cout<<"get_range->"<<std::endl;
      return arr;
      }
      
      int main(void)
      {
      for(auto val:get_range())
      {
        std::cout<<val<<std::endl;
      }
      return 0;
      }
      //输出结果:
      //get_range->
      //1
      //2
      //3
      //4

      从上面可以看出:

      • 不论基于范围的for循环迭代多少次,get_range只在第一次迭代之前被调用
      • 对于基于范围的for循环而言,冒号后面的表达式 只会被执行一次
      • 通普通的for循环一样,如果 在迭代的过程中修改容器可能会引起迭代器失效
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值