C++11一些零碎的知识点介绍

目录

一.列表初始化

1.{}初始化

2.initializer_list

二.变量类似推导(声明)

1.auto

2.decltype

3.nullptr

三.范围for

四.STL容器新增

1.unordered_set和unordered_map

2.array

3.forward_list 

五.可变参数模板

1.递归函数方式展开参数包

2.逗号表达式展开参数包

3.STL中的emplace


前言:这里都是C++11的一些相对比较零碎或者简单的知识点,而一些重要的知识点(比如右值引用、lambda表达式、智能指针等之后再介绍)

C++11第2篇:C++11重要知识点介绍_糖果雨滴a的博客-CSDN博客

一.列表初始化

1.{}初始化

        在C++98中,可以用{}对数组或者结构体元素进行统一的列表初始值设定比如:

struct Point
{
	int _x;
	int _y;
};

int main()
{
    Point p = { 1, 2 };
    int array1[]{ 1, 2, 3, 4, 5 };
    int array2[5]{ 0 }

    return 0;
}

        C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

struct Point
{
    int _x;
    int _y;
};

int main()
{
    int x1 = 1;
    int x2{ 2 };
    int array1[]{ 1, 2, 3, 4, 5 };
    int array2[5]{ 0 };
    Point p{ 1, 2 };
    // C++11中列表初始化也可以适用于new表达式中
    int* pa = new int[4]{ 0 };

    return 0;
}

        而C++11支持这一特性,最主要的是可以应用在各种容器中,例如:

int main()
{
    vector<int> v = { 1,2,3,4 };
    list<int> lt = { 1,2 };

    // 这里{}会先初始化构造一个pair对象
    map<string, string> dict = { {"erase", "删除"}, {"insert", "插入"} };

    // 使用大括号对容器赋值
    v = {10, 20, 30};

    return 0;
}

2.initializer_list

        这个就是容器可以使用{}进行赋值的C++11中新增加的{}的类型。

        这个一般是作为构造函数的参数,C++11对STL中的不少容器都增加了intializer_list作为参数的构造函数,这样就初始化容器对象更方便了,也可以作为operator=(赋值运算符重载)的参数。

        让自己模拟实现的vector也支持{}初始化和赋值:

namespace hb
{
	template<class T>
	class vector {
	public:
		typedef T* iterator;

		vector(initializer_list<T> l)
		{
			_start = new T[l.size()];
			_finish = _start + l.size();
			_endofstorage = _start + l.size();

			iterator vit = _start;

			for (auto e : l)
				*vit++ = e;
		}

		vector<T>& operator=(initializer_list<T> l) {
			vector<T> tmp(l);
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_endofstorage, tmp._endofstorage);

			return *this;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

二.变量类似推导(声明)

1.auto

        用于实现自动类型推断。要求必须进行显示初始化,让编译器将定义对象的类型设置位初始化值的类型。

int a = 10;
auto p = &a;

2.decltype

        可以将变量的类型声明为表达式指定的类型。

int x = 1;
double y = 2.2;

decltype(x * y) ret; // ret的类型就是double

3.nullptr

        NULL被定义成的是字面量0,这样可能会带来一些问题,因为0既能表示指针常量,又能表示整型常量。因此为了更清晰和安全的角度考虑,C++11新增了nullptr,用来表示空指针。

三.范围for

        范围for是一种遍历的方式,并且代码很简洁,底层是基于STL的迭代器。只要那个容器支持迭代器,就一定支持范围for。

int main()
{
    vector<int> v{ 1, 2, 3, 4, 5 };
    for(auto e : v)
    {
        cout << e << ' ';
    }

    return 0;
}

四.STL容器新增

1.unordered_set和unordered_map

        这两个基于哈希表实现的容器是C++11中新出的两个很有用处的容器,具体使用已经在前两个博客中介绍了。

        C++ 哈希表及unordered_set + unordered_map容器_糖果雨滴a的博客-CSDN博客

2.array

        这个新增容器用处不是很大,就是一个静态数组。不过相对于正常创建的静态数组,使用这个容器访问会更加安全,一旦越界就会报错。不过vector同样越界会报错,因此我们一般使用的都是vector,很少会用array。

3.forward_list 

        这个新增容器也是如此,用处并不大。forward_list就是单链表,而list是双链表,forward_list只不过比list要少存储一个指针,能减少一点存储占用。

五.可变参数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

        上面的参数args前面有省略号,所以它就是一个可变模板参数,我们把带省略号的参数称为”参数包“,它里面包含了0到N个模板参数。

        我们无法直接获取参数包args中的每个参数,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模板参数的一个主要特点。因此如何展开可变模板参数是最大的难点。

        语法是不支持使用args[i]这样方式获取可变参数的。

因此我们有这样几种方法:

1.递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
    cout << t << endl;
}

// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
    cout << value << " ";
    ShowList(args...);
}

int main()
{
    ShowList(1);
    ShowList(1, 'A');
    ShowList(1, 'A', std::string("sort"));

    return 0;
}

        这个就是通过递归的方式,每次让参数包中的第一个类型进入模板T中,然后剩下的参数进入参数包中,以进行下一次递归,而进入模板T中的就可以得到了。而如果只剩下一个参数时,这时这个参数会进入T中,而参数包就变成了0,如果不去实现上面的那个只有T的函数,就会导致报错,因为参数包中已经没有参数了,无法进行递归了。

        

void ShowList()
{}

        上面那个函数也可以写成这样,这时当参数包为0时再次递归就会进入到这个函数,也可以结束。

2.逗号表达式展开参数包

template <class T>
void PrintArg(T t)
{
    cout << t << " ";
}

//展开函数
template <class ...Args>
void ShowList(Args... args)
{
    int arr[] = { (PrintArg(args), 0)... };
    cout << endl;
}

int main()
{
    ShowList(1);
    ShowList(1, 'A');
    ShowList(1, 'A', std::string("sort"));

    return 0;
}

        这种方式是通过PrintArg函数,先进入到函数中,得到每一个参数包中的值,然后因为我们不知道参数包中的类型是什么,所以我们通过逗号表达式的方法,将每一个数都变成0,然后存入arr中,这样就不会因为不知道类型而直接将值存入数组导致报错。

        也可以写成这样:

template <class T>
void PrintArg(T t)
{
    cout << t << " ";

    return 0;
}

//展开函数
template <class ...Args>
void ShowList(Args... args)
{
    int arr[] = { PrintArg(args)... };
    cout << endl;
}

int main()
{
    ShowList(1);
    ShowList(1, 'A');
    ShowList(1, 'A', std::string("sort"));

    return 0;
}

        这里可以不用逗号表达式,而是让上面的函数直接返回0。

3.STL中的emplace

template <class... Args>
void emplace_back (Args&&... args);

        因为有了参数包和万能引用(万能引用在接下来的右值引用中介绍)等,就有了emplace的出现。而相对于原本的push_back,insert等,emplace的接口又有什么优势呢?

        emplace系列的接口对于右值,是直接构造,而push_back则需要先构造,再进行移动构造。

        emplace的效率要稍微高那么一点,不过并不是很大。而用法上几乎一样。

int main()
{
    std::list< std::pair<int, bit::string> > mylist;
    mylist.emplace_back(10, "sort");
    mylist.emplace_back(make_pair(20, "sort"));
    mylist.push_back(make_pair(30, "sort"));
    mylist.push_back({ 40, "sort"});

    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰果滴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值