19C++11重要新特性

本文详细介绍了C++11的一些重要新特性,包括类内初始化、nullptr的使用、constexpr变量、类型别名声明、auto类型指示符、decltype类型指示符、范围for语句、容器的cbegin和cend函数、右值引用、列表初始化返回值、尾置返回类型、默认构造函数、委托构造函数、string对象处理文件名、标准库initializer_list类、array和forward_list容器、非成员函数swap、容器insert成员的返回类型、emplace成员的返回类型、shrink_to_fit函数以及数值转换函数。这些特性增强了C++的效率和安全性。

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;

右值引用:常量,不过可以引用无名的临时变量,另外根据猜测的特性,临时变量不立即销毁。

&& 和左值引用的区别

  1. 绑定的对象(引用的对象)不同,左值引用绑定的是返回左值引用的函数、赋值、下标、解引用、前置递增递减
  2. 左值持久,右值短暂,右值只能绑定到临时对象,所引用的对象将要销毁或该对象没有其他用户
  3. 使用右值引用的代码可以自由的接管所引用对象的内容

类型 && 引用名 = 右值表达式; //用的不多,一般用于减少拷贝次数。


#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转换为字符串

27.未完结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值