c++ primer c++11 字符串 向量 数组 表达式 运算符优先级结合律 各种语句基本写法 03-05

23 篇文章 0 订阅
9 篇文章 0 订阅

03

  • 每个名字都需要独立的using声明

  • 头文件不应该包含using声明 因为头文件的内容会拷贝到其他文件中去 可能污染名字空间产生冲突

  • 使用=一般是拷贝初始化 其他方式一般是直接初始化

  • 通过 cin 输入 string 对象时 会自动u忽略开头的空白(空格换行制表符等等)直到遇见下一处空白
    比如输入" Hello World! “时结果是"Hello”

  • getline(cin, string)可以读取一整行 直到遇到换行符结束 读取到换行时停止读取 但是换行并咩有被写入string中

  • cctype中有很多大小写判断相关接口哦

  • 建议使用c++版本的标准库头文件
    c++兼容c时 c语言的头文件为name.h c++将这些文件命名为cname 在name前添加了c前缀 去掉了.h后缀
    cname与name.h的内容是一样的 但是cname的名字空间都在std中 .h的则需要记住名字空间在哪里

  • c++标准库string与c的string头文件include

    #include <string>
    #include <cstring>
    
  • vector不能用下标增加元素只能用来访问已经存在的元素

  • 在for循环中尽量使用!=而不是小于<
    因为标准库容器的迭代器都定义了==!=但是他们中的大多数都没定义<运算符

  • const_iterator的cit只能读vector<string>中的元素 但是it既可以读也可以写
    cbegin和cend可以返回const_iterator的迭代器

    vector<string> v(10, "av");
    vector<string>::const_iterator cit;
    vector<string>::iterator it;
    it = v.begin();
    cit = v.begin();
    (*it) = "A";
    for (auto s : v)
    {
        cout << s << " " << flush;
    }
    
string初始化的几种方式
    string s1;
    string s2(s1);
    string s3 = s2;
    string s4("value");
    string s5 = "value";
    int n = 10;
    string s6(n, 'c');
for的简单形式 使用&取地址符表示引用可以修改
    string s = "jsjhsafhjsajk";
    for (auto c : s)
    {
        cout << c << flush;
    }
    cout << endl;

    for (auto &c : s)
    {
        c = toupper(c);
    }
    cout << s << endl;
vector初始化的几种方式
    int n = 10;
    int val = 1;
    vector<int> v1;
    vector<int> v2(v1);
    vector<int> v3 = v2;
    vector<int> v4(n, val);
    vector<int> v5(n);
    vector<int> v6{1, 2, 3};
    vector<int> v7 = {4, 5, 6};
值初始化

通常情况 只提供vector对象容纳的元素数量而省略初始值 此时库会创建一个值初始化的元素初值覆给容器中的所有元素
如果vector的元素类型是内置类型 比如int则元素初始值自动设为0

花括号的列表初始化的值可能不符合初始化条件

花括号列表的值不能初始化时会尝试构造函数进行构造

    vector<string> v1{10};
    vector<string> v2{10, "av"};
    // 这里的10不能初始化string 所以会尝试构造函数
    for (auto s : v2)
    {
        cout << s << " " << flush;
    }
	
	输出结果
	av av av av av av av av av av 
数组
  • 数组的元素个数必须编译时已知 必须是常量表达式

  • 默认初始化的规则 比如在函数内部定义了某种内置类型的数组 那么值都是为定义的 gg

  • 数组的元素为对象 所以不存在引用的数组

  • 数组的类型不允许使用auto推导

  • 如果提供的初始值比维度小 那么其他的值默认值初始化

    	// 这三个是一样的效果
    	string s1[3] = {"he", "wor"};
    	string s2[3] = {"he", "wor", ""};
    	string s3[3] = {"he", "wor", ""};
    
  • 不允许拷贝和赋值
    不允许用一个数组初始化另一个数组
    不允许把一个数组直接赋值给另一个数组

  • 复杂的声明 从右到左 解释

    int *(&array)[10] = ptrs;
    1. 是一个引用 因为有括号
    2. int *[10]
    3. 是一个数组的引用 是一个有10个元素的数组引用
    4. 数组的元素指针 数组的元素是int的指针
    5. 最后 array 是一个有10int*元素的数组的引用
    
  • 使用数组下标时 通常定义为size_t类型 在cstddef中有size_t的定义

  • 用到数组名字的地方会 编译器会自动将其替换为一个指向数组首元素的指针 p和p2和nums都相等

        string nums[] = {"one", "two", "three"};
        string *p = &nums[0];
        string *p2 = nums;
        cout << (p == p2) << " " << (p == nums) << endl;
    
  • auto对数组的推断是指针非数组 decltype对数组的推断就是数组不会发生上述的转换

  • 获取数组的尾指针int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};不能对尾指针解引用等等等等等
    直接获取 int *e = &arr[10]
    c++11引入了两个名为beginend的函数可以数组进行使用begin(arr) end(arr)这样子

  • 数组的下标可以为负数

多维数组 其实就是数组的数组
  • 数组的阅读有内而外分析

  • 初始化以及如何使用for进行循环 提供的初始化数据不如维度的个数时 所以其他的部分执行值初始化(前面说道)

    int arr1[3][4] = {0};
    int arr2[3][4] = {
        {1, 2},
        {4, 5},
    };
    for (const auto &row : arr1)
    {
        for (auto v : row)
        {
            cout << v << " " << flush;
        }
        cout << endl;
    }
    for (auto &row : arr2)
    {
        for (const auto v : row)
        {
            cout << v << " " << flush;
        }
        cout << endl;
    }
    
  • 上述的for如果在row的auto处不加上&符号 然后auto会自动将row转换为首地址的指针(在auto一节有说)
    如下代码将会无法编译通过 使用for循环多维数组的时候除了最内层的循环 其他所有循环都需要是引用类型

        for (auto row : arr2)
        {
            for (const auto v : row)
            {
                cout << v << " " << flush;
            }
            cout << endl;
        }
    
  • 多维数组指针别名简化 p是一个行数组的指针 所以*p是行数组首地址 *p+4是行数组尾指针

    // 这俩声明等价等价等价
    using int_array1 = int[4];
    typedef int int_array2[4];
    
    using int_array_4 = int[4];
    int ia[3][4] = {1, 2, 3, 4, 7, 8, 9, 0, 4, 5, 6, 7};
    for (int_array_4 *p = ia; p != ia + 3; ++p)
    {
        for (int *q = *p; q != *p + 4; ++q)
        {
            cout << *q << " " << flush;
        }
        cout << endl;
    }
    
C风格字符串
  • #include <cstring>

  • 实际是 const char * 类型

  • 永远记住最后的\0结尾

  • string对象不能用来初始化char或者const char对象 但是string.c_str()返回一个const char指针可以用来初始化const char对象
    但是c_str()的返回值是无法一直保持有效的 如果s发生了变化 那么之前返回的c_str可能会失效
    如果程序想要一直可以使用c_str最好是将该数组拷贝一份

  • 可以使用数组初始化vector


04

  • 算数运算符左结合率

  • 多数情况下 没有明确求值的顺序 int i = f1() * f2() 并不会对f1和f2的求值顺序进行要求

  • 短路求值 &&与运算 ||或运算 ?:三目运算 ,逗号运算 规定了求值顺序

  • 例子 f() + g() * h() + j()
    优先级 先算乘法
    结合率 从左到右加
    但是 函数的调用顺序 其实没有明确规定

  • 但是这个顺序 编译器很有可能优化了

  • 关于 / 除法 和 % 求余
    定义满足 (m/n)*n+m%n=m
    如果 m%n!=0 那结果一定与m的符号相同 即如下
    m/-n -m/n 的商都是 -(m/n)
    m%-n 符号与m相同 结果= m%n
    -m%n 符号与-m相同 结果= -(m%n)

  • 前置递增递减返回左值 后置递增递减返回右值

  • 后置递增运算符的优先级高于解引用运算符即 *it++ 相当于 *(it++)

  • sizeof运算符 返回字节数 sizeof不需要实际计算其运算对象的值 所以 sizeof *p 即使p已经不合法了也可以求sizeof
    sizeof对数组执行可以求得数组所占空间大小 不会把数组转换为指针处理 所以得到了一种计算数组大小的方法
    sizeof(数组)/sizeof(数组类型)

  • 逗号运算符的结果是右侧表达式的值

  • P147运算符优先级和结合率表

类型转换
  • 主要是整型提升 int unsigned int long unsigned long long long unsigned long long

  • 如果一个是无符号一个是带符号的分两种情况

    • 无符号类型不小于带符号类型 则转换为无符号的 和提升一样

    • 如果带符号类型大于无符号类型 则依赖于机器 哪个大转换为哪个
      比如一个long和一个unsigned int
      如果long和unsigned int的大小一样 则long转换为unsigned int
      如果long比unsigned int的大 则unsigned int转换为long

  • 0或nullptr可以转换为任意指针类型

  • static_cast<T> 不包含底层const才可以使用这个

  • const_cast<T> 只能改变底层const 不能改变类型 但是去掉const属性很有可能导致未定义结果

  • reinterpret_cast 高危操作

  • dynamic_cast 后面有介绍

  • 旧的强制转换 type(expr) 函数式的强制转换 (type)expr C风格的强制类型转换

    const char *cp;
    // 正确 去掉const属性 未改变类型 但是修改*p1可能导致未定义结果 毕竟之前是const
    char *p1 = const_cast<char *>(cp);
    // 错误 改变了类型const_cast只能改变const属性
    string p2 = const_cast<string>(cp);
    //错误 static_cast不能改变const属性
    char *p3 = static_cast<char *>(cp);
    // 正确
    string p4 = static_cast<string>(cp);
    

05

  • 语句case
    最重要的 一旦匹配了case 那么将从该标签开始执行后续所有的case分支 除非显示的中断 这也就是为什么尽量给每一个case都尽量break
    case的值一定是整型常量表达式 所以 switch 的值必须可以转换为整型
    default标签

    int v = 3;
    switch (v)
    {
    case 0:
        cout << 0 << endl;
        break;
    case 1:
        cout << 1 << endl;
        break;
    case 2:
        cout << 2 << endl;
        break;
    case 3:
        cout << 3 << endl;
        break;
    case 4:
        cout << 4 << endl;
        break;
    default:
        cout << "default" << endl;
        break;
    }
    cout << "-----------------" << endl;
    switch (v)
    {
    case 0:
        cout << 0 << endl;
    case 1:
        cout << 1 << endl;
    case 2:
        cout << 2 << endl;
    case 3:
        cout << 3 << endl;
    case 4:
        cout << 4 << endl;
    default:
        cout << "default" << endl;
    }
    cout << "-----------------" << endl;
    // 根据没有break会继续往下执行 可以对多种情况进行一样的处理
    // 如下 这样 0 1 2 就会进行相同的处理啦 3 4 5也是同样啦
    // 就算没有换行也是一样的
    switch (v)
    {
    case 0:
    case 1:
    case 2:
        break;
    case 3: case 4: case 5:
        break;
    case 6:
        break;
    default:
        break;
    }
    
    结果如下
    3
    -----------------
    3
    4
    default
    -----------------
    
  • while 和 for
    定义在while(或者for)内(条件内或者循环体内)的变量每次迭代都会经历创建到销毁的过程

  • break只能 for while do while switch

  • continue 只能 for while do while switch嵌套在循环内部也可以在switch中使用continue

  • goto 需要lable标签 goto语句和转向的标签语句必须在同一个函数内
    标签需要用冒号:表示 示例如下

    int test_goto()
    {
        int f = 1;
    flag1:
        cout << "goto" << endl;
        if (f)
        {
            f = 0;
            goto flag1;
        }
        else
        {
            cout << "done" << endl;
        }
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值