Title: c++ 的类型回顾
date: 2017/9/10 17:38:58
categories:

  • 编程
    tags:
  • cpp
  • 类型

C++标准规定了每个算术类型的最小存储空间,但它并不阻止编译器使用更大的存储空间。事实上,对于 int 类型,几乎所有的编译器使用的存储空间都比所要求的(16字节)大。

c语言和c++的内存管理

c语言和c++的内存管理

  • c语言分四个区:堆,栈,全局区和常量区
  • c++分五个区: 堆、栈、全局区、常量区和自由存储区
    c++的自由存取区类似于c语言的堆,存放的是malloc创建的内存,

基本类型

字符类型
字符类型有两种:char 和 wchar_t。char 类型保证了有足够的空间,能够存储机器基本字符集中任何字符相应的数值,因此,char 类型通常是单个机器字节(byte)。wchar_t 类型用于扩展字符集,比如汉字和日语,这些字符集中的一些字符不能用单个 char 表示。
整数型
short、int 和 long 类型都表示整型值,存储空间的大小不同。一般, short类型为半个机器字长,int 类型为一个机器字长,而 long 类型为一个或两个机器字长(在 32 位机器中 int 类型和 long 类型通常字长是相同的)。
浮点型
对于实际的程序来说,float 类型精度通常是不够的——float 型只能保证 6 位有效数字,而 double 型至少可以保证 10 位有效数字,能满足大多数计算的需要。

字面量

只有内置类型存在字面值,没有类类型的字面值。因此,也没有任何标准库类型的字面值。
整型字面值
整型字面量可以有三种进制的表示:十进制、八进制和十六进制。以 0(零)开头的字面值整数常量表示八进制,以 0x 或 0X 开头的表示十六进制。

字面值整数常量的类型默认为 int 或 long 类型。其精度类型决定于字面值——其值适合 int 就是 int 类型,比 int 大的值就是 long 类型。通过增加后缀,能够强制将字面值整数常量转换,在数值后面加 L 或者 l(字母“l”大写或小写)指定常量为 long 类型。在数值后面加 U 或 u 定义 unsigned 类型。同时加 L 和 U就能够得到 unsigned long 类型的字面值常量 。

用十进制或者科学计数法来表示浮点字面值常量。使用科学计数法时,指数用 E 或者 e 表示。默认的浮点字面值常量为 double 类型。在数值的后面加上 F 或 f 表示单精度。同样加上 L 或者 l 表示扩展精度. 1e-3f 表示的是0.001.
字符型字面量
在字符字面值前加 L 就能够得到 wchar_t类型的宽字符字面值。L'a'
字符串字面值
字符串字面值常量用双引号括起来的零个或者多个字符表示。为了兼容 C 语言,C++ 中所有的字符串字面值都由编译器自动在末尾添加一个空字符。"A" 表示包含字母 A 和空字符两个字符的字符串。 也存在宽字符串字面值,一样在前面加“L”,如 L"a wide string literal"宽字符串字面值是一串常量宽字符,同样以一个宽空字符结束。

两个相邻的仅由空格、制表符或换行符分开的字符串字面值(或宽字符串字面值),可连接成一个新字符串字面值。这使得多行书写长字符串字面值变得简单:

string a = "123"
           "456";

变量

变量名规则
变量名必须以字母或下划线开头,并且区分大小写字母。char _;
初始化多个变量
当一个定义中定义了两个以上变量的时候,每个变量都可能有自己的初始化式。 对象的名字立即变成可见,所以可以用同一个定义中前面已定义变量的值初始化后面的变量。

double salary = 9999.99, wage(salary + 0.01); 

内置类型的变量只有在全局定义时才会被自动初始化为0, 而类的对象定义时如果没有指定构造函数,不管是全局定义还是局部定义都会调用默认构造函数。

引用类型

引用(非const型)必须用与该引用同类型的对象初始化。
定义多个引用
在一行中定义多个引用时,每个名字签名前都必须带&,这一点与指针相似,因为它们都是复合类型

int i=1024, j=2048;
int &a=i, &b=j;

const引用
const引用是指向const对象的引用,不能用非const引用指向const对象,const引用当然也可以指向非const对象的引用。

非const引用只能绑定到与该引用同类型的对象。const引用则可以绑定到不同但相关类型的对象(多态或支持隐式类型转换)或绑定到字面值常量上(这就是有限的两个引用不同类型的例子)。

const int a = 1;
int b = 2;
int &c=a; //会报错
const int &d=b ; //正确
const int &e = a; //正确
const int &f = 1; // 正确
int &g = 1; // 错误
auto &h=a; //正确 auto会保留底层const 
auto &i=b; // 正确
auto &j=1; //错误,字面值需要指明const
const auto &k=1; //正确

对比,最后两个表达式,我们知道编译器可以从右值的对象中推断出底层const,却无法从字面量中推出,所以字面量必须显式指出const auto;

当指向不同类型的对象时,会临时创建一个对象进行类型转换。

指针类型

指针与引用的区别

  1. 指针是一个对象,所以可以有赋值、取地址等操作,所以可以有指向指针的引用。
  2. 不强制必须初始化

除了两种例外情况外,其它所有指针的类型都要和它所指向的对象严格匹配
例外1. const对象的指针指向同类型的非const对象(比引用严格,引用可以不同类型)
例外2. 多态的场合

空指针
c++11建议 使用字面值常量nullptr来将一个指针初始化为空指针

int *a=nullptr;

等价于 int *a=0;
以前的程序用的NULL其实是一个预处理变量,这个变量定义在头文件cstdlib中,
c++建议初始化所有的指针,而常量指针在定义的时候必须初始化。

class与 struct

用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private——《c++ primer》

类的定义
类的定义可以放在头文件里面。

头文件一般不包含定义,只包含extern 变量的声明和函数的声明。有三个例外:类的定义、值在编译时就知道的const对象和inline函数——《c++ primer》

当我们在头文件中定义了const变量后,每个包含这个头文件的源文件都有了自己的const变量,当该const变量是用常量表达式初始化时,编译器在编译时会用常量表达式替换这些const变量,所以不会为每个源文件创建const对象的存储空间。而当是用函数返回值或其它内容对const变量进行初始化时,编译期间并不知道其值,所以就必须为其分配存储空间,同时该const变量也不能定义在头文件中。

类型别名

typedef和using都能定义类型别名

typedef double wages;
using wages=double;

如果某个类型别名指代的是符合类型或常量,那么把它用到声明语句中就会产生意想不到的后果。

char tmp='a';
typedef char *  pstring;
const pstring cstr=&tmp;
*cstr='b';
cstr=nullptr;
pstring const cstr2=&tmp;
*cstr2='b';
cstr2=nullptr;

其实cstr1和cstr2是同样类型的对象。

auto

C++ 是静态类型(statically typed)语言,在编译时执行类型检查。结果是程序中使用某个名字之前,必须先告知编译器该名字的类型——《c++ primer》

auto的对象必须初始化,auto能在一条语句中,声明多个变量。一条声明语句只能由一个基本数据类型。auto可以推断出指针,却推不出引用,必须用auto &来定义一个引用。

auto i = 0, *p = &i;

auto的对象的类型有时候会和初始值的类型不完全一致
auto与引用
当用来为auto对象初始化的值是引用类型的时候,auto对象的类型是真正参与初始化的对象的值。
设置一个类型为auto的引用时,初始值终点顶层常量属性仍然保留

const int a = 0;
int b = 0;
auto &b = a; // b是const int 类型的引用
auto c = a;   // c是int类型
auto *d = &a, &e = b; // 编译出出错,因为&a是const int的地址,而b是int

auto与const
auto一般会忽略掉顶层const,同时底层const会被保留,比如当初始值是一个指向常量的指针时。

const int ci = 0, &cr = ci; 
auto a = &ci; // 这时候a是const int * 类型的
auto b = cr; // b是int类型的 
auto &c = ci; // c是const int & 类型的,因为是底层const 

如果希望推断出的auto类型是一个顶层const,则需要明确指出。

const auto f = ci;

decltype

c++11 引入了第二种类型说明符 decltype,它的作用是返回表达式返回值的类型,但是不会实际计算表达式的值。

decltype 遇到解引用操作将返回引用类型,引用类型的变量定义的时候必须初始化

int a;
int *p=&a;
decltype(*p) c=a; //注意这里必须初始化

decltype与变量,decltype((var))返回的是引用类型,decltype(var)返回的是正常类型。