You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
对象在使用之前需要先初始化,c++中有两种非常常用的初始化方式:
= , eg int a = 1; 这种方式继承于C,
{}, eg int a{1}; 这种方式是c++推荐使用的。
为什么标准要搞一套新东西?
= 继承于c,没有强制类型检查,int a = 3.2;会触发隐式转换,最后a是3
{},int a{3.2}; 会报错,因为会丢失精度
这是为了兼容c而引入"缩小转换"+"隐式转换",使用{}是推荐的
常量需要初始化,变量只有在极少场景下可以不初始化
一个对象,只有有了合适的值,才有意义,对机器来说,
声明了不定义(也就是不初始化,没有分配内存,也就没有相应的值)
对机器来说,就是没有太大意义,定义才有意义
定义的过程会触发对象的初始化
定义一个变量时,如果通过初始化能推导出类型,就不毕显示指定类型
auto i = 12; // int
auto b = true; // bool
auto a = 'a'; // char
使用auto时,初始化方式推荐使用 = , 因为此时不会有隐式转换
此时并不是说不适合用{}初始化方式,都是可以的
没有特殊原因需要显示指明类型,推荐使用auto,特殊原因一般是:
定义块太长,有个明显的类型方便阅读
想要明确对象的范围或精度 eg double a{1};
auto的好处:减少冗余;类型很长的时候用auto很方便
更大的好处在与泛型编程时,类型实在是太长了
作用域和声明周期 scope and lifetime
作用域:
local scope,局部作用域,出现在函数体内和lambda内,包括了函数参数
class scope, 类作用域,类成员都在这个作用域里
namespace scope, 命名空间作用域
如果一个成员不在{}里,称为全局变量、全局函数,
也可以认为在全局命令空间作用域
上面说的都是有名字的对象,有名字,肯定会出现在某一作用域里
还有一些对象是没有名字的:临时变量和new创建的对象
对象在使用之前需要先构造(初始化),在作用域结束时会析构(销毁)
namespace scope里的对象的析构在程序结束时,
class scope里的对象,析构在对象释放时
new创建的对象,在delete时释放
常量
c++中支持两种概念上的常量:
const:表示不会改变这个对象的值,一般用在接口上
表示传给函数的指针或引用不会被修改
编译器看到了会用const来保证不会修改值
const修饰的值,可在运行时计算
constexpr:新标准搞出来的东西,表示会在编译期计算值
主要用在特定的常量,将数据放在只读内存,防止被修改
好处是性能提升了,因为编译期就把值计算出来了
一般的常量(不局限于c++)分两种:物理不变、逻辑不变
c++采用的是物理不变,即内存中常量对象的值不变
实现方式是通过编译器的const机制来实现,
任何修改内存值的操作都是违规的
const的重点是值不会变
const int a = 1; 编译器确定常量值
const int b = fun(); 运行器确定常量值
constexpr是const的更进一步:编译器就确定常量值
优化程度更高,c++11和c++14描述的规则不一样
如果一个函数要在常量表达式中使用,那么这个函数要用constexpr修饰
有这个constexpr,就是告诉编译器在编译期就计算
constexpr函数可以当做非常量参数,此时结果就不是一个常量表达式
constexpr函数的参数可以不是常量表达式,此时函数就和普通函数一样
结果是一个非常量表达式
好处是不必为了"constexpr函数"和"普通函数"定义两次
constexpr的好处好像蛮多,是不是所有的函数都可以constexpr修饰
不是,constexpr函数有要求:要简单,要没有副作用
说白了:不能修改非local变量
后面会说明需要常量表达式的场景
主要是编译器计算对性能的提升巨大
除此之外,也丰富了c++语言的特征库
指针 数组 引用
最基本的数据容器是一段连续的内存,里面放同样类型的数据,
也称为数组
也是硬件所提供的方式
char v[6];
char* p = &v[5];
char x = *p;
[] 读array of
char*的*读:pointer to
*p,*在前面表示: contents of
&在前面表示:address of
for(int 1 = 0; i < 10; i++)怎么读:
set i to zero;while i is not 10;increment i;
上面的for loop是一种方式,新标准提供了一种更简单的方式:
for (auto x : vec)
称为 range for 语句
上面的范围for语句怎么读:
for every element of vec,from the first to the last ...
for (auto& x : ver)
&读reference to
称为引用
引用和指针类似
只是指针对象前要加*才能访问对象
引用使用时,对象一定要初始完成
函数传参,使用const 引用,比较常用,也能节省开销
在声明中, & * [] 称为声明操作符
std::cout << "123";
std::cin >> x;
<< 读 put to
>> 读 get from
if (auto x = fun(); 0 != x) {}
x的作用域是if分支,和for语句中声明变量一样
好处是限定x的作用域。
因为if已经包含了0比较和nullptr比较
所以可以省略成 if (auto x = func()){}
这样性能略高一点
硬件映射
当我们使用一些基础操作时,都是硬件提供的实现,
像+-*/都是调用机器指令,通过机器指令去操作硬件
所以最基本的操作都是由硬件来提供的
指针,就是内存中的机器地址
就是像类似的硬件映射,在底层的性能上让c、c++领先其他语言几十年
c c++的计算机制是基于硬件的,而不是基于数学
assign操作,也就是赋值操作,
对基本类型来说,就是一个拷贝操作
初始化initialization是不同于赋值操作的
赋值的对象都是有值(从底层看,是分配了内存,内存里是有东西的)
初始化的对象初始化之前是没有分配内存
对绝大部分类型来说,对未初始化的变量进行读写,结果都是未知
内置类型更明显
int a = 1;
int& b = a; // b初始化 不是赋值
初始化和赋值的不同,更多体现在自定义类型中
设计良好的自定义类型,最好提供= 和 == 操作
函数的参数和返回值,语义上都是初始化
数字分隔符 '
π = 3.14159'26535'89793