Table of Contents
6.1.1 Implementations
6.2 Types
6.2.1 Fundamental Types
6.2.2 Booleans
6.2.3 Character Types
6.2.3.1 Signed adn Unsigned Characters
6.2.3.2 Character Literals
6.2.8 Sizes
6.2.9 Alignment
6.3 Declarations
6.3.1 declaration 的结构
6.3.4 Scope
6.3.5 Initialization
6.3.5.1 Missing initializers
6.3.6.1 The auto Type Specifier
6.3.6.3 The decltype() Specifier
6.4 Objects and Values
6.4.2 Lifetime of Objects
6.5 Type Aliases
C++ 的实现可以是hosted或者freestanding. hosted实现所有标准中描述的标准库. freestanding可以提供更少的标准库,只要
所有name(名称)都要有一个type(类型)与它对应. type决定了可以对name实行什么样操作.
integeral types: boolean, character, integer.
arithmetic types: integral and floating-point.
user-defined types: Enumerations and classes.
built-in types: fundamental types, pointers, references.
根据定义,当转换为整数时, true为1,false为0. 反过来,当整数转换(可以隐式转换)为bool时; 非零整数转换为true; 0 转换为false.
用{}-initializer 可以来阻止narrowing:
bool b1 = 7; // true
bool b2{7}; // error
bool b3{7!=0} // true
char, signed char, unsigned char, wchar_t(大小是implementation defined), char16_t(16bit), char32_t(32t).
char是否有符号是implementation defined. 只要char表示字符的数值在0-127之间,就没什么大问题.
signed 和 unsigned还有char之间的指针不能隐式转换.
enclosed by ''
'\x05f'
L'a' 是wchar_t类型
U'' 4byte的字符
u'' 2byte的字符
C++中一个对象的大小是用char的大小来表示的. 根据定义,char的大小为1.
1 = sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
1 <= sizeof(bool) <= sizeof(long)
sizeof(char)<= sizeof(wchar_t) <= sizeof(long)
sizeof(float) <= sizeof(double) <= sizeof(long double)
char至少有8bits (typically 8 bits), short至少16bits, long至少32bits.
如果需要指定的大小的int,
alignof()返回表达式的alignment的要求
在一个name(identifier)被使用之前,它必须被声明. 有很多declaration也同时是definition. 一个definition是一个declaration同时提供了程序该如何使用的信息.
在global scope中
char ch;
auto count =1;
const char* name ="XXX";
struct Date{int d, m, y;};
int foo(){return 2}
//上面的是definition, 设定了内存空间
using Point = std::complex;
//上面这个也是definition
//下面这些只是declaration
double sqrt(double);
extern int error_number;
struct User;
可以有多个声明但是只能有一个定义
ps:我之前以为全局中的,比如 int x,算作定义,局部变量中的int x只能算声明,但是想想应该不是这样的. 首先如果有类A, 局部变量 A x;的话x在声明后就调用constructor初始化了,所以算作definition. 同样 int x; 也应该被算作初始化了,只不过没有指定值,所以应该也算definition.
可选的prefix specifier(static, virtual, extern) base type(const int, vector
declarator 包含了 一个可选的名字name, 和一些declarator operator.
prefix operator: *(pointer), *const (constant pointer), * volatile(volatile pointer), & (lvalue reference), && (rvalue reference), auto
postfix operator: [], (), ->
postfix比prefix绑定的更紧. 比如 char *kings[]. kings是数组还是指针呢? 因为postfix绑定的更紧,所以应该先读[], 所以kings是数组,数组的元素类型是 char*. char (*kings)[], 这个用了括号,先读括号内的, 所以kings是指针, 指向一个char的数组
scope就是一个name可以被使用的区域了.
Local scope:在函数或者lambda里声明的名字叫做local name, 它的scope从声明开始到所属的block结束.函数参数的被认为是local name,scope是函数最外层的block.
Class scope: 在一个class中但是在任何函数,其他class,enum class, 其他名称空间外声明的名字叫做member name, scope从class的声明{ 到结束}
Namespace scope:在一个名称空间内但是在任何函数,lambda,class, enum class, 其他名称空间外声明的name 叫做namespace member name. scope从declaration开始到他所属的namespace 结束. 这个name也可以被其他的translation unit取用.
Global scope:在所有namespace, class, enum class, namespace, ... 之外声明的name叫做global name. scope从声明开始到所属的文件结束.也可以被其他文件取用.
Statement scope: 在for-, while-, if-, switch- 的 ()中定义的. scope从声明开始到语句结束. 也是local name.
Function scope:从声明到函数结束.
在一个block(包括statement)里声明的name可以隐藏它所在的block之外的相同的name. 如果一个global name被隐藏了,可以通过::前缀来取用.比如
int x;
void foo()
{
int x= 1;
::x = 2;//global x
x=2;
}
非class成员的名字的声明开始指的是在declarator 之后,initializer之前. 比如
int x =99;
void foo()
{
int x= x;//在 int x 的时候就声明了local name x, 等号后面的x此时指的是local name x而不是global x
}
X a1{v};
X a2={v};
X a3= v;
X a4(v);
第一种是C++11新加的list initialization, 基本上可以用于任何情况. 用{}可以防止narrowing.
如果用name{}的话就是默认值.
但是有些情况,比如 auto a1{99}; 这种情况 auto会变成initializer_list
如果global,namespace, local static, static member没有指定initializer的话, 会自动用{} 等价的初始化.
在堆里创造的变量默认没有初始化,除非是用户定义的有构造函数的类型. 如果要指定的话可以用{}, 比如 new int{10}.
在stack上的,自动变量,没有指定initializer, 没有well-defined 值.
基本上指定initializer的情况就是弄一个buffer. 比如 char buffer[4096];. 这个buffer也可以初始化 char buffer[4096]{}, 这样所有元素都是0, 这种初始化会话费时间,所以要考虑一下到底需不需要.
如果一个变量的声明有initializer, 可以使用auto来使编译器自动推导类型
可以用const, & 等来修饰auto.
注意: 一个表达式的类型不可能是引用, reference在expression里被隐式地dereference了.
void g(int& v)
{
auto x = v; // 表达式"v"的类型是int 而不是 int&
auto& y = v; // y的类型是int&
}
decltype(expr) 反映expr的type,这个type是编译器已经知道的类型.所以这个是编译期决定的,所以expr不会在运行时被evaluate一遍.
lvalue指的是 在内存中被分配了空间 的 object. rvalue指的是不是lvalue的东西, 比如一些临时值,函数返回值. 没有移动语义之前 lvalue指的是那种可以放在等号左边的东西,rvalue就是不能放在等号左边的东西. 注意这里我说的等号是指默认的等号操作符,如果你overload自己的=操作符,那么用这个被你overload的=操作符的rvalue是可以放在等号左边的.
PS:个人的想法. temporary value实际上肯定会在内存中分配空间的, 但是关键是这种value生命周期太短了,只是用于一瞬间的运算或者移动. 有人会说那函数返回值可以绑定到一个变量上啊比如 int x = foo(); 注意, foo()返回的值是rvalue, 这个rvalue用来初始化x而不是绑定到x上,所以x和foo()在理论上不是同一个物体. 另外有些人也可能会说 咱们可以用const lvalue reference 或者rvalue reference来绑定rvalue呀,比如 bar(int&& x)或者bar(const int& x); 这样rvalue不就有名字x了吗, 有名字不就是lvalue了吗. 没错,确实x,这个rvalue reference 延长了rvalue的生命周期,使得你可以对rvalue做一些lvalue才能做的操作.等bar返回了以后,x引用的rvalue还是会死. 为什么这么麻烦? 因为傻逼,没有什么为什么,给编译器实现者参考的,程序员只用了解下rvalue,lvalue,移动语义这些就行了. 其实不止l,rvalue, 他们还有xvalue, prvalue, glvalue. std::move()实际上是把lvalue变成xvalue,而不是rvalue, 算了,傻逼,别多想了,越想越麻烦.
Automatic:从定义开始,离开scope结束.
Static: 直到程序结束.
Free store: new开始 delete结束
Temporary objects:临时的物体,就刚刚讲的,如果有引用来引用它,它的生命周期就是引用的声明周期. 如果没有引用,那它在包含它的一整串expression结束后死掉.
Thread local:
using Pchar = char*; //pointer
using PF = int (*) (double); //pointer to a function
template
class sb{
using value_type = T;
};
template
using Vector = std::vector>;
//typedef
typedef int int32_t; // == using int32_t = int;
typedef void (*foo)(int); // == using foo = void (*)(int);