C++11新特性介绍(二)

前言

该篇文章讲述C++11新特性中

①列表初始化

(1)列表初始化的好处

(2)必须使用列表初始化的一些场合

(3)列表初始化中的顺序

②列表初始化防止类型收窄

③范围for循环

列表初始化

C++11推出了一种可以让我们更方便,更高效的初始化方法:列表初始化

构造函数的两个阶段

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,先初始化阶段,后计算阶段

关键点

所有类类型的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的函数体中

初始化列表可以提升我们程序的性能,对于内置类型用不用初始化列表可能没什么区别,但如果是初始化自定义数据类型,可以少调用一次默认构造函数,在数据量非常大的时候,可以有效的帮助我们提升程序的运行效率

先来看个例子,Person类定义了默认构造函数,拷贝构造函数以及显示重载了赋值运算符,主要是为了方便结果的查看

class Person
{
public:
	Person()
	{
		cout << "Person默认构造函数调用" << endl;
	}
	Person(const Person& p)
	{
		this->a = p.a;
		cout << "Person拷贝构造函数调用" << endl;
	}
	Person& operator=(const Person& p) 
	{
		this->a = p.a;
		cout << "Person重载赋值运算符调用" << endl;
		return *this;
	}
	int a;
};

自定义了 Text类,有Person类型的成员变量p,构造函数函数体内初始化p

class Text
{
public:
	Person p;
	Text(Person& p1)
	{
		p = p1;
	}
};

主函数调用如下代码

int main()
{
	Person p;
	Text t(p);
	return 0;
}

程序运行结果:

①Person p; 调用一次默认构造

② 初始化阶段对Text类的成员进行初始化,Text里的Person p;调用一次默认构造

③p=p1,重载赋值运算符的调用

那么如果使用初始化列表初始化自定义类型的数据,主函数不变,结果是怎么样的呢?

class Text
{
public:
	Person p;
	Text(Person& p1):p(p1){}
};

程序运行结果:

主函数代码是没变的,但运行结果完全不同,发现少调用了一次默认构造函数,因为在初始化列表中,会省去默认构造函数的过程,直接调用拷贝构造函数初始化p,所以我们在编写代码过程中,能用初始化列表就尽量使用初始化列表

必须使用初始化列表的一些场合

①常量成员初始化,因为常量成员只能初始化不能赋值,所以必须在初始化列表中

class Person
{
public:
	Person(int num)
	{
		a = num;//错误,a是常量,不能被赋值
	}
	const int a = 10;
};
class Person
{
public:
	Person(int num):a(num){} //正确,初始化列表
	const int a = 10;
};
int main()
{
   Person p(12);
   cout<<p.a<<endl;//a的值为12
}

②没有默认构造函数的类类型,因为使用初始化列表可以不调用默认构造函数,直接在初始化列表中调用拷贝构造函数

class Person
{
  public:
  Person(int num):a(num){}
  int a;
};
class Text
{
  Person p;
  Text(Person& p1)
  {
     p = p1;
  }
};

这段代码是错误的,因为Person没有默认的构造函数,所以Text的成员Person p初始化时就会编译不通过,使用初始化列表就可以解决这个问题

class Person
{
  public:
  Person(int num):a(num){}
  int a;
};
class Text
{
  Person p;
  Text(Person& p1):p(p1){}
};

③引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以需要初始化列表

列表初始化中的顺序

成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的

class Person
{
 public:
 Person(int i):a(i),b(a){} //正确,先用i初始化a,再用a初始化b
 int a;
 int b;
};
class Person
{
 public:
 Person(int i):b(i),a(b){} //错误,用b初始化a,但b此时未定义
 int a;
 int b;
};

因为a,b定义是a先于b,所以列表初始化先初始化a,用b初始化a,但此时b是未定义的,所以错误

列表初始化防止类型收窄

什么是类型收窄?可以简单理解为精度丢失

int a = 1024;
char c = a;
printf("%d\n",c);

上面代码不会报错,编译可以通过,但c输出值为0,为什么?因为char在内存中占1个字节,8位,转成int值最大表示为127,1024装不下呀,所以这时候就会出现类型收窄,但又不报错,咋办?

int a = 1024;
char c = {a};

使用初始化列表就可以避免这种问题,因为编译都通过不了,会告诉你这需要收缩转换,当然也不是所有编译器都支持这样,有些编译器还是允许通过的,例如qt

范围for循环

这是C++11引进的更为简单的for循环语句

//基本语法
for(变量:序列)
{
   函数体
}

序列:可以是用花括号括起来的初始值列表、数组或者vector或string等类型的对象,这些类型的共同特点是拥有能返回迭代器的begin和end成员,并且序列的范围要确定才能使用(形参中的数组不是数组,是指针变量,无法确定范围)

void func(int a[])
{
  for(auto k:a)//错误,形参中的数组不是数组,是指针变量,无法确定范围
   ...
}

变量:序列中的每个元素都能转换成该变量的类型。确保类型相容最简单的办法是使用auto类型,这个关键字可以让编译器帮助我们指定合适的类型。如果需要对序列中的元素执行写操作,循环变量必须声明成引用类型

函数体:执行的操作

vector<int> vec = {1,2,3,4,5,6};
//传统写法
for (auto it = vec.begin(); it != vec.end(); it++)
{
	cout << *it;
}
//范围for循环
for (auto it : vec) //相当于创建了一个局部变量,自动(it++)向后遍历
{
	cout << it;
}
for (auto &it : vec) //引用更高效,并且可以改变原数组
{
	cout << it;
}

 这篇文章对你有帮助的话~就点个赞吧~

点赞收藏关注就是对我最大的支持~

一起学习C++11新特性~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值