[读书笔记]学习C++ - Types and Declarations

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




6.1.1 Implementations

C++ 的实现可以是hosted或者freestanding. hosted实现所有标准中描述的标准库. freestanding可以提供更少的标准库,只要, , , , , , , , , , , , , , 提供了就行.

6.2 Types

所有name(名称)都要有一个type(类型)与它对应. type决定了可以对name实行什么样操作.

6.2.1 Fundamental Types

integeral types: boolean, character, integer.

arithmetic types: integral and floating-point.

user-defined types: Enumerations and classes.

built-in types: fundamental types, pointers, references.

6.2.2 Booleans

根据定义,当转换为整数时, true为1,false为0. 反过来,当整数转换(可以隐式转换)为bool时; 非零整数转换为true; 0 转换为false.

用{}-initializer 可以来阻止narrowing:

bool b1 = 7; // true

bool b2{7}; // error

bool b3{7!=0} // true

6.2.3 Character Types

char, signed char, unsigned char, wchar_t(大小是implementation defined), char16_t(16bit), char32_t(32t).

char是否有符号是implementation defined. 只要char表示字符的数值在0-127之间,就没什么大问题.

6.2.3.1 Signed adn Unsigned Characters

signed 和 unsigned还有char之间的指针不能隐式转换.

6.2.3.2 Character Literals

enclosed by ''

'\x05f'

L'a' 是wchar_t类型

U'' 4byte的字符

u'' 2byte的字符

6.2.8 Sizes

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, 里面包含了各种大小的int.

6.2.9 Alignment

alignof()返回表达式的alignment的要求

6.3 Declarations

在一个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.

6.3.1 declaration 的结构

可选的prefix specifier(static, virtual, extern)    base type(const int, vector)    declarator(可选是否包含名字, p[7], n, *(*)[])    可选的suffix function specifiers(const, noexcept)    可选的initializer或者function body(={7,5,5} 或者 { return 233;}).

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的数组

6.3.4 Scope

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
}

6.3.5 Initialization

X a1{v};
X a2={v};
X a3= v;
X a4(v);

第一种是C++11新加的list initialization, 基本上可以用于任何情况. 用{}可以防止narrowing.

如果用name{}的话就是默认值.

但是有些情况,比如 auto a1{99}; 这种情况 auto会变成initializer_list,可能不是我们想要的,所以用auto的时候偏向于使用 =. 另一种情况,vector v1{99}, 这种被解释为vector里初始化添加一个99, vector v1(99) 调用构造函数,初始化99个int在vector中. 产生这种情况的原因是vector 有接收initializer_list的构造函数,如果没有的话v1{99}就会被解释为调用接收一个int的构造函数.

6.3.5.1 Missing initializers

如果global,namespace, local static, static member没有指定initializer的话, 会自动用{} 等价的初始化.

在堆里创造的变量默认没有初始化,除非是用户定义的有构造函数的类型. 如果要指定的话可以用{}, 比如 new int{10}.

在stack上的,自动变量,没有指定initializer, 没有well-defined 值.

基本上指定initializer的情况就是弄一个buffer. 比如 char buffer[4096];. 这个buffer也可以初始化 char buffer[4096]{}, 这样所有元素都是0, 这种初始化会话费时间,所以要考虑一下到底需不需要.

6.3.6.1 The auto Type Specifier

如果一个变量的声明有initializer, 可以使用auto来使编译器自动推导类型

可以用const, & 等来修饰auto.

注意: 一个表达式的类型不可能是引用, reference在expression里被隐式地dereference了.

void g(int& v)
{
    auto x = v; // 表达式"v"的类型是int 而不是 int&
    auto& y = v; // y的类型是int&
}

6.3.6.3 The decltype() Specifier

decltype(expr) 反映expr的type,这个type是编译器已经知道的类型.所以这个是编译期决定的,所以expr不会在运行时被evaluate一遍.

6.4 Objects and Values

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, 算了,傻逼,别多想了,越想越麻烦.

6.4.2 Lifetime of Objects

Automatic:从定义开始,离开scope结束.

Static: 直到程序结束.

Free store: new开始 delete结束

Temporary objects:临时的物体,就刚刚讲的,如果有引用来引用它,它的生命周期就是引用的声明周期. 如果没有引用,那它在包含它的一整串expression结束后死掉.

Thread local:

6.5 Type Aliases

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);

 

你可能感兴趣的:(C++,读书笔记)