《Beginning C++20 From Novice to Professional》第三章Working with Fundamental Data Types

本章能够学到的是

基本是上一章基本类型的延伸,讲了一些和变量相关更接近编程语言语法的东西,和计算不太相关

除了基本类型还有一些其他类型和技术可以用

Operator Precedence and Associativity 操作符优先级和结合律

precedence又叫priority,指的是运算符的优先级

associativity指的是结合律、结合性,即从左向右运算还是从右向左

有了这两个性质我们才可以决定复杂表达式中的运算顺序

Nearly all operator groups are left-associative

so most expressions involving operators of equal precedence are evaluated from left to right.

 

大多数运算符都是左结合性,基本都是从左向右看的,这样也比较符合我们的阅读习惯与理解

表3-1中的每一个序号都表示优先级,数字越小优先级越高,同一个优先级可能有很多运算符

当我们不确定优先级的时候加括号就好了

但是一些常见的优先级还是有必要了解,否则容易出错:

  1. 逗号优先级最低
  2. 后缀自增自减优先级高于前缀版本
  3. 四则运算优于等号优于逻辑运算

Bitwise Operators 位运算

they’re usually applied to unsigned integer types. 

位操作一般都用于颜色RGB、flag变量、字体等,用某位表示信息的变量

The Bitwise Shift Operators 移动操作

可以看到对无符号数左移两位首先导致溢出的数字被舍弃:Bits that fall off either end of the variable are lost.

其次左右移的操作数有一个2,2是int类型,所以表达式中的short先转为int,我们得到原类型还要手动转换

如果不进行转换,结果是这样的:

shifting n bits to the left is equivalent to multiplying by 2, n times. In other words, it’s equivalent to multiplying by 2n. Similarly, shifting right n bits is equivalent to dividing by 2n.

移位和乘除没有区别,可能更快

Shifting Signed Integers 有符号数左右移 

特别拿出来讲就是因为移位操作在有无符号数的结果不同,有符号数有一位用来保存符号,我们不能简单的把溢出的位给舍弃,此时进行的移位称为算术移位

有符号数的右移不会导致符号位改变,结果都是预期中的

左移操作则和无符号数相同,损失的位就直接丢掉

因此有符号数不适合操作位数据,同时书中也建议使用std::byte <cstddef>替代unsigned char

Logical Operations on Bit Patterns 按位逻辑运算

注意不要与条件的逻辑操作符号混淆

与:同真则真;或:同假则假;异或:不同则真

这里不多讲了,与或非大家都多少了解一点

The Lifetime of a Variable 变量生存期

通常叫生存期的东西:lifetime或者storage duration

自动生存期

(automatic或 local)

{}块内定义的非静态对象,又叫局部变量

Variables defined within a block that are not defined to be static

从定义开始到块结束

They exist from the point at which they are defined until the end of the block, which is the closing curly brace, }.

拥有局部作用域或块作用域

local scope、block scope

静态生存期

(static)

static关键字定义的对象

Variables defined using the static keyword have static storage duration

从定义开始到程序结束

Static variables exist from the point at which they are defined
and continue in existence until the program ends.

动态生存期

(dynamic)

运行时定义/分配内存的对象

Variables for which you allocate memory at runtime have dynamic storage duration.

从定义开始到析构结束

They exist from the point at which you create them until you release their memory to
destroy them.

线程局部生存期

(thread_local)

thread_local关键字定义的对象

Variables declared with the thread_local keyword have thread storage duration.

(本书未介绍)

thread_local 变量的生存期与其所在线程的生存期一致


另外一个性质叫作用域scope,这个东西是变量名字生效的地方,只有在变量的作用域里我们才可以使用变量的名字,或者说这个变量才可以被访问被使用

It’s important not to get these two ideas confused.

虽然两者紧密相关,但是生存期和作用域不是一件事,在变量生存期不代表在它的作用域

Global Variables 全局变量

Variables defined outside of all blocks and classes are also called globals and have global scope (which is also called global namespace scope).

所有块外、类外的对象都是全局对象,拥有全局作用域

Global variables have static storage duration by default 

全局对象默认都是静态生存期,且如果不初始化都会进行零初始化,而不是像局部对象一样不初始化

这张图说明了一个小程序里不同的作用域

访问全局变量我们可以用全局作用域::,全局作用域本身没有名字,所以不像std::

此外同一个作用域里能访问局部变量和全局变量时,同名的局部变量会覆盖全局变量,类内也是一样

# include <numbers>
# include <iostream>
# include <format>
# include <cmath>
using namespace std;
long count1{999L};   // Global count1
double count2{3.14}; // Global count2
int count3;          // Global count3 - default initialization
int main() {
    /* Function scope starts here */
    int count1{10}; // Hides global count1
    int count3{50}; // Hides global count3
    std::cout << "Value of outer count1 = " << count1 << std::endl;
    std::cout << "Value of global count1 = " << ::count1 << std::endl;
    std::cout << "Value of global count2 = " << count2 << std::endl;
    {
        /* New block scope starts here... */
        int count1{20}; // This is a new variable that hides the outer count1
        int count2{30}; // This hides global count2
        std::cout << "\nValue of inner count1 = " << count1 << std::endl;
        std::cout << "Value of global count1 = " << ::count1 << std::endl;
        std::cout << "Value of inner count2 = " << count2 << std::endl;
        std::cout << "Value of global count2 = " << ::count2 << std::endl;
        count1 = ::count1 + 3; // This sets inner count1 to global count1+3
        ++::count1;            // This changes global count1
        std::cout << "\nValue of inner count1 = " << count1 << std::endl;
        std::cout << "Value of global count1 = " << ::count1 << std::endl;
        count3 += count2; // Increments outer count3 by inner count2;
        int count4{};
    } /* ...and ends here. */
    // std::cout << count4 << std::endl; // count4 does not exist in this scope!
    std::cout << "\nValue of outer count1 = " << count1 << std::endl
            << "Value of outer count3 = " << count3 << std::endl;
    std::cout << "Value of global count3 = " << ::count3 << std::endl;
    std::cout << "Value of global count2 = " << count2 << std::endl;
} /* Function scope ends here */

Value of outer count1 = 10
Value of global count1 = 999
Value of global count2 = 3.14

Value of inner count1 = 20
Value of global count1 = 999
Value of inner count2 = 30
Value of global count2 = 3.14

Value of inner count1 = 1002
Value of global count1 = 1000

Value of outer count1 = 10
Value of outer count3 = 80
Value of global count3 = 0
Value of global count2 = 3.14

 Enumerated Data Types 枚举

先简单举个例子,枚举可以将类型限制到一个范围里

不过虽然我们给了初始值是int,枚举和int仍然不是一个类型,不可以参与计算及输入输出

Values for enumerators must be compile-time constants, that is, constant expressions that the compiler can evaluate.

枚举初值必须是编译期可知的值,不过不止可以用int,任何指定的整型都可以

# include <numbers>
# include <iostream>
# include <format>
# include <cmath>
using namespace std;

int main() {
    enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
    Day yesterday{Day::Monday}, today{Day::Tuesday}, tomorrow{Day::Wednesday};
    const Day poets_day{Day::Friday};
    enum class Punctuation : char { Comma = ',', Exclamation = '!', Question = '?' };
    Punctuation ch{Punctuation::Comma};
    //默认输出用int转,指定了类型之后就用其他类型,比如标点用char
    std::cout << std::format("yesterday's value is {}{} but poets_day's is {}{}\n",
                             static_cast<int>(yesterday), static_cast<char>(ch),
                             static_cast<int>(poets_day), static_cast<char>(Punctuation::Exclamation));
    today    = Day::Thursday;         // Assign new ...
    ch       = Punctuation::Question; // ... enumerator values
    tomorrow = poets_day;             // Copy enumerator value
    std::cout << std::format("Is today's value({}) the same as poets_day({}){}\n",
                             static_cast<int>(today), static_cast<int>(poets_day), static_cast<char>(ch));
    // ch = tomorrow;                 /* Uncomment any of these for an error */
    // tomorrow = Friday;
    // today = 6;
    // 1.不同类型之间不能赋值
    // 2.Friday属于枚举作用域,此处不可见,需要用Day::Friday
    // 3.枚举不是int,不可以当做int
}

不过Friday不可见的原因和cout必须要加std::的原因一样

我们也可以using namespace Day来解决这个问题


C风格枚举不需要加class关键字,此时就可以当成int处理,而且也没有作用域的问题

但是C++不推荐这么写

Aliases for Data Types 类型别名

这点不多说了,总之using比typedef可读性要高不少

可以看之前《Primer》的文章

第二章 变量和基本类型-CSDN博客


EXERCISES

3-1

可以看到计算机是如何求一个数的相反数的,无论正负所有位取反再加一

3-2

计算一层长方形架子能装多少正方形盒子

长和宽是double类型的英尺,盒子边长为int类型的英寸,输出结果为long类型

3-6

只使用一个复合运算符来交换两个数

这种做法还是挺有意思的,不需要第三个变量也不使用swap函数

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

+xiaowenhao+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值