C++11——初始化列表,变量类型推到,默认成员函数控制
Posted 两片空白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11——初始化列表,变量类型推到,默认成员函数控制相关的知识,希望对你有一定的参考价值。
目录
一.初始化列表
1.1 C++98的初始化
在C++98中,数组可以用"{}"整体初始化。
比如:
int arr[] = {1,2,3,4,5,6};
但是对于自定义类型却不能用"{}"进行初始化。
比如:
vector<int> v = {1,2,3,4,5,6};//编译错误
使得我们必须先将自定义类型定义出来,再将每一个成员变量插入进去。导致很不方便
1.2 C++11的初始化
针对上面这种情况,C++11增加了可以使用"{}"来初始化自定义类型的功能。使得内置类型和自定义类型都可以使用"{}"来进行初始化。
int main(){
//内置类型
int a{ 10 };
int b = { 10 };
int c{ 1 + 2 };
//自定义类型
vector<int> vv1{ 1, 2, 3, 4, 5, 6 };
vector<int> vv2 = { 1, 2, 3, 4, 5, 6 };
list<int> l1{ 1, 2, 3, 4, 5, 6 };
list<int> l2 = { 1, 2, 3, 4, 5, 6 };
map<string, string> dict{ { "left", "左" }, { "right", "右" }, { "up", "上" } };
//动态数组,C++11支持,C++98不支持
int *arr = new int[6]{1, 2, 3, 4, 5, 6};
system("pause");
return 0;
}
注意:C++11是再C++98基础上进行升级,C++98的用法还可以使用。
初始化列表可以再"{}"前使用等号,效果和不使用等号相同。
1.3 自定义类型的初始化
- 自定义类型具体数量的列表初始化
当我们自定义的类型的成员变量是一个具体数字时,我们直接调用列表初始化
class Test{
private:
int _x;
int _y;
int _z;
public:
Test(int x = 0, int y = 0, int z = 0)
:_x(x)
, _y(y)
, _z(z)
{}
};
int main(){
//调用构造函数
Test t0(4, 5, 6);
Test t1{ 1, 2 };
Test t2{ 1, 2, 3 };
//Test t3{ 1, 2, 3, 4 };//编译错误,不能大于Test成员变量数
system("pause");
return 0;
}
但是列表里的值不能大于Test成员变量数,否则会编译错误。
错误信息为:
- 当自定义类型的成员变量是动态的,可以取多个值时
想要支持可以在初始化时列表输入多个值,需要在类中重载一个带有initializer_list类型参数的构造函数。
class String{
private:
char *_str;//大小是动态的
int _capacity;
int _size;
public:
//这样就可以使用{}进行初始化
String(initializer_list<char> str)
:_capacity(str.size())
, _size(0)
{
_str = new char[_capacity];
for (auto ch : str){
_str[_size++] = ch;
}
}
String(char *str = nullptr, int cap = 0, int size = 0)
:_str(str)
, _capacity(cap)
, _size(size)
{}
};
int main(){
String sh{ 'a', 'b', 'c', 'd','\\0' };
return 0;
}
initializer_list作用:
- initializer_list是一个轻量级容器类型
- 可以接受任意长度的初始化列表,但是元素必须是要相同的或者可以转换为T类型的。
- 只有三个成员接口,begin(),end(),size(),其中size()返回initialzer-list的长度。
- 只能被整体的初始化和赋值,遍历只能通过begin和end迭代器来,遍历取得的数据是可读的,是不能对单个进行修改的。
此外initialzer-list<T>保存的是T类型的引用,并不对T类型的数据进行拷贝,因此需要注意变量的生存期。比如我们不能这样使用:
std::initializer_list<int> func(void)
{
auto a = 2, b = 3;
return{ a, b };
}
虽然看起来没有任何问题,且能正常编译通过,但是a,b是在func内定义的局部变量,但程序离开func时变量a,b就销毁了,initialzer-list却保存的是变量的引用,因此返回的将是非法未知的内容。
二.变量类型推导
2.1 为什么需要变量类型推导
在定义变量时,我们必须给出变量的类型,编译器猜允许定义。但是当我们不知道需要的实际类型该怎么给,或者类型写起来很复杂时。不方便程序员编写。
比如:
int main(){
char a = 125;
char b = 125;
//超出char最大范围,写成char接收造成数据丢失。如果让编译器来推导出实际类型,不会存在数据丢失
char c = a + b;
std::map<std::string, std::string> dict{ { "left", "左" }, { "right", "右" }, { "up", "上" } };
//写起来很麻烦
std::map<std::string, std::string>::iterator it = dict.begin();
while (it != dict.end()){
std::cout << it->first << ":" << it->second << std::endl;
it++;
}
return 0;
}
在C++11中可以使用auto来进行类型推导,使得程序的书写更加方便。比如:
但是auto的使用也右一些不足,使用auto的对象必须先进行初始化,否则编译器无法推导出auto的实际类型。并且auto不能推导函数返回值类型和函数参数类型。
在表达式运行完之后进行类型推导,运行完才知道结果的实际类型,为运行时类型识别(RTTI:Run-Time Type Identification)。
在C++98中已经支持RTTI:
- typeid只能查看类型,不能用结果类型定义对象
- dynamic_cast只能应用于含有虚函数的继承体系中。
运行时类型识别的缺陷是降低程序运行效率。
2.2 decltype类型推导
decltype是根据表达式和函数返回推演出实际类型,可以用来定义变量。
- 推演表达式的类型作为变量的定义类型
- 推演出函数返回值的类型
三.默认成员函数控制
3.1 显示缺省函数
一个类会默认生成6大成员函数,构造函数,拷贝构造函数,析构函数,取地址函数,const取地址函数。当我们显示定义时,编译器就不会生成对应的函数。
有一种情况:当我们显示出带参数不带缺省的构造函数时,编译器不会默认生成构造函数,当我们需要实例化无参对象时,会因为没有的构造函数而报错。
在C++11中,我们可以在默认函数定义或者生命时加上=default,从而显示的指示编译器生成该函数的默认版本。用=default修饰的函数称为显示缺省函数。
3.2 删除默认函数
在C++98中,如果想限制外部使用默认函数,可以将函数设置成私有private,并不给定义。在外部调用时,就会报错。
在C++11中,在默认函数后面加上=delete,编译器就不会生成对应的默认函数。称=delete修饰的函数为删除函数。
注意:避免删除函数和explicit一起使用
以上是关于C++11——初始化列表,变量类型推到,默认成员函数控制的主要内容,如果未能解决你的问题,请参考以下文章