C++ 处理类型引入的关键字;引用、指针、const

typedefauto以及decltype是C++处理类型的几个关键字

1. 类型别名

类型的名字可能非常复杂,所以C++提供了两种创建类型别名的方式,传统的typedef和C++11中引入的using方式,下面是例子:

typedef double my_double;

using mydouble = double;

这两种方式都可以创建别名,别名与类型的名字等价。

2. auto关键字

auto是C++11引入的类型说明符,它可以自动的获得变量的类型 - 显然此时我们必须给变量提供初始值。

auto num = 0; // num自动获取为int类型
auto num;     // 错误,因为没有提供初始值

关于auto自动绑定,当涉及到引用的时候会变得有些复杂:

- auto会忽略顶层(top-level)const:

int i = 0;
const int ci = i, &cr = ci;   // cr是ci的别名
auto b = ci;                  // 整数,顶层的const被忽略掉了
auto c = cr;                  // 整数,同上因为cr是ci别名,ci本身就是个顶层const
auto d = &i;                  // 整型指针
auto e = &ci;                 // 整数常量指针 - 对常量对象取地址是一种底层const

 (顶层const:指针本身是个常量;底层const:指针指向的对象是一个常量)

- 希望推断出来的auto类型是个顶层const:

const auto f = ci;

- 将引用的类型设为auto:

auto &g = ci;  // g是整型常量引用,绑定到ci (即const引用)
auto &h = 42;  // 错误,不能将非常量引用绑定到字面值(即&h不是const引用)
const auto &j = 42; // 正确,可以将常量引用绑定到字面值

注意左值引用(left reference)必须被初始化,否则是错误的。引用就是已经存在的对象的别名。 

- 放在同一行使用的时候需要保证这一行的类型是一样的:

auto k = ci, &l = i;      // k是int(忽略了ci的顶层const),l是int引用所以也是int
auto &m = ci, *p = &ci;   // m是int引用,p是int指针,&ci是取地址
auto &n = i, *p2 = &ci;   // 不对了,i是int,n是int引用(等价于int),*p2却是个const int

看到这里可能观众对于引用和指针产生了一点疑惑,尤其是 为啥auto &m = ci, *p = &ci; 是合法的,这里说明一点:auto后面的m和*p是并列来看的,m作为int引用,与*p所表示的int是同种类型。这里千万不要认为m和p是同类型,不是的。这里m是int引用,而p是int指针;编译器比较的实际上是m和*p。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int i = 0, &r = i;
	auto a = r;
	const int ci = i, &cr = ci;
	auto b = ci;
	auto c = cr;
	auto d = &i;
	auto e = &ci;
	const auto f = ci;
	auto &g = ci;
	const auto &h = 0;
	const auto &j = 0;
	
	auto k = ci, &l = i;
	auto &m = ci, *p = &ci;
	auto &n = i;
	auto *p2 = &ci;
	cout << a << b << c << *d << *e << f << g << h << j << k << l << m << n << *p << *p2 << endl;
}

 看上面这段代码,m和*p是并列的。指针本身作为一种复合类型,比较复杂;引用也是一种类型,叫做引用类型(听君一席话如听一席话),这里m的类型是const int &, p的类型是const int *。做不到,没办法去定义引用的引用,因为引用本身不是一个对象 - 尝试对引用创建引用只能创建出引用的对象的引用,而不是引用本身的引用。(绕口令,好好寻思寻思)

下面这段小练习挺有意义的:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int i = 0, &r = i;
	auto a = r;
	const int ci = i, &cr = ci;
	auto b = ci;
	auto c = cr;
	auto d = &i;
	auto e = &ci;
	const auto f = ci;
	auto &g = ci;
	const auto &h = 0;
	const auto &j = 0;
	
	auto k = ci, &l = i;
    auto kk = ci, *ll = &i;
	auto &m = ci, *p = &ci;
	auto &n = i;
	auto *p2 = &ci;
	cout << a << b << c << *d << *e << f << g << h << j << k << l << m << n << *p << *p2 << endl;
	a = 42;  //y int类型可以修改
	b = 42;  //y 顶层const被忽略,auto判断出来的是int类型,可以修改
	c = 42;  //n 不能修改const int 引用 y 同上
	//d = 42;  //?? 可以改但是把值赋给指针类型?N, invalid conversion from ‘int’ to ‘int*’ [-fpermissive]
	//e = 42;  //同上 N invalid conversion from ‘int’ to ‘const int*’ [-fpermissive]
	//g = 42;  //n不能修改const int 引用
}

3. decltype类型指示符

C++11 引入的除了auto之外的类型说明符decltype的作用是从表达式的类型推断出变量的类型。它在后面与string相关的地方会有很大用处(避免了无法判断.size()函数返回的是int还是unsigned_int的尴尬 - 实际上是string::size_type类型)。

decltype(f()) sum = x;

编译器不会实际调用f,只把f返回值的类型作为sum的类型。

- 右值*在指针前面叫做解引用符号。

- decltype和auto的不同,decltype不会忽略顶层const

const int ci = 0; &cj = ci;
decltype(ci) x = 0;  // x是个const int类型
decltype(cj) y = x;  // y是个const int &类型,绑定到x上
decltype(cj) z;   // 错误,没有初始化z,z是个引用

- 其实decltype还解答了关于指针解引用之后类型的疑惑,

int i = 42, *p = &i, &r = i;
decltype(r + 0) b;  // 正确,r + 0表达式类型是int
decltype(*p) c;     // 错误,c是int &类型,必须初始化

解引用指针可以得到指针所指的对象,还能给这个对象赋值。因此decltype(*p)的类型是int &而不是int。

- 变量如果加上括号,会被decltype判断成表达式,因为变量可以作为赋值表达式左值,赋值表达式本身是左值的引用,即如果i是int,那么i = x赋值表达式的类型就是int &,所以(i)也被当成了int &。

decltype((i)) d; // 错误,d是int &
decltype(i) e;   // 正确,e是int (未初始化)

关于decltype的一个简单应用,书上85页的例子:

string s("some string");
for (decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index) {
    s[index] = toupper(s[index]);
}

作者很喜欢这个把字母变成大写的例子,除了这里,迭代器部分也用的这个例子。还有作者很喜欢42这个数字。扯远了。

这里面index的类型就是s.size()的返回类型,因为写string::size_type或者记忆可能有难度,所以用decltype是个非常明智的做法。

类似的,可以用auto获取string::size_type类型:

string s("some string");
auto length = s.size();

今天到这里就可以了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值