C++11重要新特性
1.类内初始化
在我们之前,初始化都是在构造函数中,但是有时使用不当,部分成员没有赋值,但被使用时,就会不知道该成员指向了哪里。在许多高级开发语言中,java/go都会给成员默认添加初始值,现在C++11也可以了。
#include <iostream>
using namespace std;
class Student
{
public:
string name{"lisi"};//string name = "lisi"
int age{20};// int age = 20; //基本等价
public:
Student();
Student(string name, int age);
};
Student::Student()
{
}
Student::Student(string name, int age)
{
this->name = name;
this->age = age;
}
void test()
{
Student stu("zhangsan", 20);
cout << stu.name << endl;
Student stu2;
cout << stu2.name << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
2.nullptr
NULL是一个宏定义,NULL 在C语言中被定义为((void *)0), 在C++中被定义为字面量0。
NULL带来的问题:
字面量0的二义性,在C++98中,字面常量0的类型就可以是一个整型,也可以是一个无类型的指针(void*). 所以要想调用到func(char )这个版本函数,就必须对字面常量0进行强制类型转换【(char)0】调用,否则编译器会优先把0解析成一个整型常量。
Linux平台下编译出错的原因:g++编译器直接将NULL转换为编译器内部标识(__null),并在编译时期做了一些分析,一旦遇到二义性就停止编译向用户报告错误。虽然这在一定程度上缓解二义性带来的麻烦,但是由于标准没有认定NULL为一个编译时期的标识,所以可能带来代码移植上面的限制。
解决方案:
在C++11中nullptr是一个“指针空值类型”的常量,指针空值类型被命名为nullptr_t.
nullptr_t的定义方式与传统的先定义类型,在通过类型声明值的做法相反(充分利用了decltype返回值类型推导的功能)。
在现有的编译器情况下, 使用nullptr_t的时候必须#include(#include某些头文件的时候会间接的#include, 如#include).而nullptr则不用。这是由于nullptr是关键字,nullptr_t是通过推导得到的。
nullptr的优势:nullptr是有类型的,且仅可以被隐式类型转化为指针类型。
所有定义的nullptr_t类型的数据都是等价的,行为也是完全一致。
nullptr_t类型数据可以隐式转换为任意一个指针类型
nullptr_t不可以转换为非指针类型,即使使用reinterpret_cast()的方式
nullptr_t类型数据不适用于算术运算表达式
nullptr_t类型数据可以用于关系运算表达式,但仅能和nullptr_t类型数据和指针类型数据比较,当且仅当关系运算符为==,<=, >=时返回true
总结:1.nullptr类型是nullptr_t,可以隐式的转换为任意指针类型。NULL不行,NULLc中是无类型的指针(void*)0,需强制转换。
2.nullptr_t不可以转换为非指针类型,所以不会出现二义性问题,更加安全。
3.C++11标准规定用户不能获取nullptr的地址。
3. constexpr变量
static:
静态值,只赋值一次,不在赋值,并且只在本文件内使用。
const:
const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类的对象没被创建时,编译器不知道const数据成员的值是什么。
const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const
简单说:const修饰类中的成员只在某个对象生存期内是变量,static const修饰的可以编程全局常量。
constexpr:
constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。
强化const,等价于java中 static final 。
constexpr int *p=0; //p为常量指针,与const 不同
const int a = 20;
const int *p = &a; //p是指针常量,二者不同
4.类型别名声明
1.#define:宏定义本质替换,所以也可以定义类型的同义词;
2.typedef:传统方法;
3.using:C++11新方法
typedef ListNode<T> Node;
using uint_t = unsigned int;
#define LENGTH 10
5. auto类型指示符
变量的自动类型推断:编译时,进行推导,但是会带来不直观的问题。
6. decltype类型指示符
与auto类型,不过它是通过获取其他变量来确定类型的,而不是推导。
const int ci = 42, &cj = ci;
decltype(ci) x = 0; // x 类型为const int
auto z = ci; // z 类型为int
7.范围for语句
与java中foreach语句非常相似,使用也相似
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
string s = "string";
//一般打印字符串s的每个字母的方法
for (int i = 0; i < s.size(); i++)
{
cout << s[i] << " ";
}
//范围for语句
for (auto c : s)
{
cout << c << " ";
}
system("pause");
return 0;
}
vector容器:
int main(int argc, char const *argv[])
{
vector<int> v = {1, 2, 3};
//一般遍历
for (auto i = v.cbegin(); i != v.cend(); i++)
{
cout << *i << " ";
}
//范围for语句
for (auto i : v)
{
cout << i << " "; //注意,没有*哦
}
system("pause");
return 0;
}
8. 定义vector对象的vectort
添加了使用vec.size()获取元素的方式
int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(int i=0;i<=b.size()-1;++i){cout<<b[i]<<endl;}
迭代器
int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(vector<int>::iterator it=b.begin();it!=b.end();it++){cout<<*it<<" ";}
9. vector对象的列表初始化
添加了新的初始化方式,
1.列表初始化
vector<string> text{"ksaocdICsni"};//列表初始化
2.构造函数初始化
之前已经讲解过
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char const *argv[])
{
string str[] = {"hello", "world", "this", "find", "gank", "pink", "that", "when", "how", "cpp"};
vector<string> strArray(str, str + 10);//it.begin(),it.end()
for (vector<string>::iterator it = strArray.begin(); it != strArray.end(); it++)
{
cout << *it << " ";
}
system("pause");
return 0;
}
10. 容器的cbegin和cend函数
添加cbegin和cend,c代表的是const,表达容器数据不能被修改。
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char const *argv[])
{
const vector<int> cv{1, 2, 3, 4, 5, 6};
auto it = cv.cbegin();
while (it != cv.cend())
{
cout << *it << endl;
++it;
}
system("pause");
return 0;
}
11.除法的舍入规则
除法的舍入规则
c++11规定除法都是向0取整。(即直接删除小数部分)
12.用大括号包围的值列表赋值
vector<int> cv{1, 2, 3, 4, 5, 6};
13.将sizeof用于类成员
可以计算成员属性所需要的内存空间大小。
C++11以后,也可以这样初始化:
struct Image{
int width;
int height;
char data[10000];
};
可以这样初始化data成员:
Image image1;
memset(image1.data, 0, sizeof(iamge1.data));
C++11以后,也可以这样初始化:
Image image1;
memset(image1.data, 0, sizeof(Image::data));
注意sizeof的的参数,可以不通过对象,直接使用作用域运算符来获取成员的大小。
14.标准库initializer_list类
*initializer_list是某种类型的数组,但是内部数据都是const T类型,可以整体作为参数传递,由{}进行初始化*
cout << max({ 54,16,48,5 }) << endl; //输出54
vector<string> cities{"Beijing","Dezhou"};
struct Peo
{
std::string name;
int pos;
};
Peo p[2]{ {"abo",1},{"abo2",2} };
注意:特别注意是否为浅拷贝,并且保存的数据均为const T类型,不可以对数据进行修改
15.列表初始化返回值
vector<string> process()
{
return{"a","b","c"};
}
16.定义尾置返回类型
c++11规定了一种新的函数定义申明的方式(trailing retern type)trail:有尾部的意思
auto func(int i)->int(*)[10];//10个int* 的数组
17.使用=default生成默认构造函数
#include <iostream>
using namespace std;
class Person
{
public:
Person() = default;//告诉编译器执行默认构造函数
Person(int age) {
this->m_age = age;
}
private:
int m_age;
};
int Person::sum = 10;//类外初始化
int main()
{
Person p1;//可以使用默认构造
return 0;
}
18.类对象成员的类内初始化
仅能初始化公有public的成员
#include <iostream>
#include <vector>
using namespace std;
class Student
{
public:
string name;
int age;
};
void test()
{
Student stu = {.name = "zhangsan",
.age = 20
};
cout << stu.name << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
19.委托构造函数
其实就是java中构造函数重载
class foo {
public:
foo(int data):ma(data){}
foo(int data, int a) {
ma = data + a;
cout << "foo(int,int)" << endl;
}
foo(int data, int a, int b) :foo(data, a) {
ma += b;
cout << "foo(int,int,int)" << endl;
}
foo(int data, int a, int b, int c) :foo(data, a, b) {
ma += c;
cout << "foo(int,int,int,int)" << endl;
}
foo(int data, int a, int b, int c, int d) :foo(data, a, b, c) {
ma += d;
cout << "foo(int,int,int,int,int)" << endl;
}
void show() {
cout << "ma = " << ma << endl;
cout << "---------------------------" << endl;
}
int ma;
};
20.用string对象处理文件名
void open (const string& filename,
ios_base::openmode mode = ios_base::in);
21.array和forward_list容器
- array:大小固定,不支持插入和删除,等价于数组,支持随机访问
array<int, 3>::iterator pos1;
cout <<endl<< "a2:";
for (pos1 = a2.begin(); pos1 != a2.end(); pos1++) {
cout << *pos1 << " ";
}
- forward_list:单链表结构,只能向后迭代
只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快
22.容器的非成员函数swap
交换两个容器内容的操作保证会很快------元素本身并未被交换,swap只是交换了两个容器的内部数据结构。元素不会被移动的事实意味着,除string外,指向窗口的迭代器、引用和指针在swap操作之后都不会失效。它们仍指向swap操作之前所指向的那些元素,只是这些元素已经属于不同的容器了。与其它容器不同,对一个string调用swap会导致迭代器、引用和指针失效。
#include <algorithm>
#include <array>
using namespace std;
int main()
{
array<int, 5> arr1 = { 1,2,3,4,5 };
array<int, 5> arr2 = { 6,7,8 };
swap(arr1, arr2);
}
22.容器insert成员的返回类型
唯一的变化就是position的参数类型由发生了变化,功能相同但增加了对const_iterator的支持。这种变化适用于所有的insert方法
iterator insert (const_iterator position,
const value_type& val);
方法的返回值从void变为iterator,返回最后一个添加的元素的位置
优点:快速,多次继续上次的插入,速度更快,特别是顺序的时候。
23. 容器的emplace成员的返回类型
我们看一下push_back源码
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
——————内部调用的其实是emplace_back,它有什么优点
分析以前的容器
在调用push_front时,首先构造一个临时的Rect对象传递给push_front方法,然后在push_front的内部,在复制一个Rect对象添加到容器中。全过程会发生一次创建动作和一次拷贝动作,才能将对象的内容添加到list当中去。struct Rect
{
Rect(int l, int t, int r, int b)
:left{l}, top{t}
,right{r}, bottom{b}
{}
int left;
int top;
int right;
int bottom;
};
std::list<Rect> rlist;
rlist.push_front(Rect(10, 10, 20, 20));//在调用push_front时,首先构造一个临时的Rect对象传递给push_front方法,然后在push_front的内部,在复制一个Rect对象添加到容器中。全过程会发生一次创建动作和一次拷贝动作,才能将对象的内容添加到list当中去。
——————在调用push_front时,首先构造一个临时的Rect对象传递给push_front方法,然后在push_front的内部,在复制一个Rect对象添加到容器中。全过程会发生一次创建动作和一次拷贝动作,才能将对象的内容添加到list当中去。
解决方式1:采用引用,获取成员的右值引用成员函数解决。C++11还提供了另一种方法:emplace成员。使用这个成员可以直接传递用于生成对象的参数,对象的创建过程交给容器去执行:
24.右值引用
左值和右值的区别?
左值:能出现在赋值号左边的表达式称为“左值,一般来说,左值是可以取地址的, 右值则不可以。
函数调用的返回值若不是引用,则该函数调用就是右值
一般的“引用”都是引用变量的,而变量是左值。其中无名变量,没有=,所以是右值
右值:只能出现在赋值表达式的右边,特点是没有名字,即没有可访问的地址名,只有一个值,不可修改。更多看起来是常量。
右值引用:无名的临时变量不能出现在赋值号左边,因而是右值。右值引用就可以引用无名的临时变量
右值引用带来的优点:可以减少拷贝次数,比如23中emplace,C++11之前,需要发生创建和拷贝,还有非基本类型的拷贝函数都是如此。影响到性能。猜测,右值引用有一个特性,可以让无名的临时变量不会使用完立即销毁(代码验证过)。
普通引用 :常量指针 int * const p = &a;
右值引用:常量,不过可以引用无名的临时变量,另外根据猜测的特性,临时变量不立即销毁。
&& 和左值引用的区别
- 绑定的对象(引用的对象)不同,左值引用绑定的是返回左值引用的函数、赋值、下标、解引用、前置递增递减
- 左值持久,右值短暂,右值只能绑定到临时对象,所引用的对象将要销毁或该对象没有其他用户
- 使用右值引用的代码可以自由的接管所引用对象的内容
类型 && 引用名 = 右值表达式; //用的不多,一般用于减少拷贝次数。
#include <iostream>
using namespace std;
int main()
{
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;
a = 100;
cout << a << endl;//输出100
system("pause");
return 0;
}
25. shrink_to_fit
降低容器capicity大小,一直到size大小,c++11之前都是匿名的vector交换来实现的。
26. string的数值转换函数
stoi(str),将字符串str转换为int
stof(str),将字符串str转换为float
stod(str),将字符串str转换为double
stol(str),将字符串str转换为long
stoll(str),将字符串str转换为long long
stold(str),将字符串str转换为long double
to_string(num),将数值类型num转换为字符串
//用的不多,一般用于减少拷贝次数。
#include <iostream>
using namespace std;
int main()
{
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;
a = 100;
cout << a << endl;//输出100
system("pause");
return 0;
}
25. shrink_to_fit
降低容器capicity大小,一直到size大小,c++11之前都是匿名的vector交换来实现的。
26. string的数值转换函数
stoi(str),将字符串str转换为int
stof(str),将字符串str转换为float
stod(str),将字符串str转换为double
stol(str),将字符串str转换为long
stoll(str),将字符串str转换为long long
stold(str),将字符串str转换为long double
to_string(num),将数值类型num转换为字符串
本文详细介绍了C++11的一些重要新特性,包括类内初始化、nullptr的使用、constexpr变量、类型别名声明、auto类型指示符、decltype类型指示符、范围for语句、容器的cbegin和cend函数、右值引用、列表初始化返回值、尾置返回类型、默认构造函数、委托构造函数、string对象处理文件名、标准库initializer_list类、array和forward_list容器、非成员函数swap、容器insert成员的返回类型、emplace成员的返回类型、shrink_to_fit函数以及数值转换函数。这些特性增强了C++的效率和安全性。
2999

被折叠的 条评论
为什么被折叠?



