C++11中的新容器(array,无序容器,tuple元组)

文章目录

      • shrink_to_fit
      • array
      • forward_list
      • 无序容器
      • 元组tuple
        • 定义和初始化tuple
        • 访问tuple成员
        • tuple中成员的数量和成员类型
        • 合并tuple
        • 遍历tuple
          • C++11实现tuple的遍历
          • C++14/17遍历
        • 参考资料

shrink_to_fit

// VS2019
int main() {
	vector<int> arr(100);
	arr.emplace_back(10);
	cout << arr.size() << endl;  // 输出101
	cout << arr.capacity() << endl; // 输出150
	arr.shrink_to_fit();
	cout << arr.size() << endl;  // 输出101 
	cout << arr.capacity() << endl; // 输出101
	arr.swap(vector<int>());
	cout << arr.size() << endl;  // 输出0
	cout << arr.capacity() << endl; // 输出0
}

  从上面可以看出,在VS下vector扩容是1.5倍(Linux下是2倍)。由于扩容了之后,多出了49*sizeof(int)的无用内存,C++11提供了shrink_to_fit函数来释放这部分这部分内存,但是这个函数只是请求,并不保证一定成功。

  而在C++11之前,都知道clear()函数是清除容器内元素个数,使其为0,但是并没有释放内存,所以一般都用swap函数来释放,现在可以clear后用shrink_to_fit函数来释放内存。

array

  引入这个容器的目的就是为了替换C中的数组,由于C中的数组是没做安全检查的,所以C++提供了这个定长容器。也是为了和C++STL兼容,虽然STL也提供了通用函数begin()和end()函数来兼容传统C,但是array更规范。

int main() {
	constexpr int size = 10;
	array<int, size> arr{ 1, 2, 5, 3, 5, 1 };
	sort(arr.begin(), arr.end());
}

forward_list

  list是双向链表,而forward_list是单向链表,其效率很高,是所以容器中没有提供size()方法的容器。

无序容器

  map和set底层都是由红黑树实现,其查找/增加/删除的时间复杂度为O(log(n)),并且这两个容器是有序的,也就是元素要支持<运算符。

  C++11提供了unordered_map/set,其底层是由哈希表实现,所以时间复杂度均为O(1),而且其是无序的。

元组tuple

  tuple类似于pair的模板。每个pair的成员类型可以不相同,但是pair只有两个成员,而一个tuple可以有任意数量的成员。每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同。下面是tuple支持的操作:

定义和初始化tuple

  当我们顶一个tuple时,需要指出每个成员的类型:

tuple<int, double, size_t> threeD;  // 三个成员都被初始化为0
tuple<string, vector<double>, list<int>> someVal("constants", { 3.15,22 }, { 0,1 });

当我们创建一个tuple对象时,可以使用tuple的默认构造函数,它会对每个成员进行值初始化;也可以像someVal一样,为每个成员显式提供一个初始值。tuple这个构造函数是explicit的,所以必须直接初始化。

  类似make_pair函数,标准库也提供了make_tuple函数,用来生成tuple对象:

auto item = make_tuple("hello", 3, 3.0); // item是一个tuple类型

访问tuple成员

  pair只有两个成员,所以可以用first和second来访问,但是tuple的成员是不固定的,所以无法设置成员来访问。要访问tuple的一个成员,就要使用标准库函数模板get。为了使用get,必须要指定显式模板实参,指出我们想要访问第几个成员。我们传递给get一个tuple对象,返回指定成员的引用:

auto item = make_tuple("hello", 3, 3.0); // item是一个tuple类型
auto book = get<0>(item);  // 返回item的第一个成员

  还可以通过tie进行解包:

int main() {
	auto item = make_tuple("hello", 3, 3.0); // item是一个tuple类型
	using type = decltype(item);
	tuple_element<0, type>::type x;   // 这个用法下面会说
	tuple_element<1, type>::type y;
	tuple_element<2, type>::type z;
	tie(x, y, z) = item;
	cout << x << " " << y << " " << z << endl;  // 输出hello,3, 3
}

  解包时,我们如果只想解某个位置的值时,可以用std::ignore占位符来表示不解某个位置的值。

tie(x, ignore, ignore) = item;   // 只解第一个成员,所以无法输出y和z

  C++14可以通过类型来获取tuple成员:

int main() {
	auto item = make_tuple("hello", 3, 3.0); // item是一个tuple类型
	using type = decltype(item);
	cout << get<int>(item) << endl; // 输出3
	cout << get<double>(item) << endl; // 输出3
	cout << get<const char*>(item) << endl; // 输出hello
}

tuple中成员的数量和成员类型

  可以通过类模板tuple_size::value来得到给定tuple类型中成员的数量,可以通过decltype来得到tupleType;

  也可以通过类模板tuple_element::type来得到tuple类型中指定成员的类型:

int main() {
	auto item = make_tuple("hello", 3, 3.0); // item是一个tuple类型
	auto book = get<0>(item);  // 返回item的第一个成员
	using type = decltype(item);
	size_t sz = tuple_size<type>::value;
	cout << sz << endl;   // 输出3
    tuple_element<1, type>::type cnt = get<1>(item);  // cnt的类型是int
}

合并tuple

  可以通过tuple_cat对两个tuple进行合并。

遍历tuple

template<typename... Args>
std::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t)
{
    os << "(" << std::get<0>(t);
    for (size_t i = 1; i < sizeof...(Args) << ++i)
        os << ", " << std::get<i>(t);
    return os << "]";
}

int main(int, char**)
{
    cout << make_tuple("InsideZhang", 23, "HeNan") << endl;
            // 编译出错,局部变量i不可作为非类型模板参数
    return 0;
}

// 这时我们便需要单独把每一位的索引拿出来作为非类型模板参数进行递归调用:
template<typename Tuple>
void tuple_print(const Tuple& t, size_t N, std::ostream& os)
{
    if (N != 1)
        tuple_print(t, N-1, os);
    os << std::get<N-1>(t);
}

  以上的代码, 也即通过参数传递的方式进行的非类型模板参数的赋值,仍然编译不通过。get参数必须在编译时即为常量表达式。

C++11实现tuple的遍历
template<typename Tuple, size_t N>
struct tuple_print {
    static void print(const Tuple& t, ostream& os) {
        tuple_print<Tuple, N - 1>::print(t, os);
        os << ", " << get<N - 1>(t);
    }
};
// 类模板的特化版本
template<typename Tuple>
struct tuple_print<Tuple, 1> {
    static void print(const Tuple& t, ostream& os) {
        os << "(" << get<0>(t);
    }
};

// operator<<
template<typename... Args>
ostream& operator << (ostream & os, const tuple<Args...> & t) {
    tuple_print<decltype(t), sizeof...(Args)>::print(t, os);
    return os << ")";
}


int main() {
    auto t1 = std::make_tuple("InsideZhang", 23, "HeNan");
    auto t2 = std::make_tuple("InsideLi", 23, "AnHui");
    cout << std::tuple_cat(t1, t2) << endl;
    //  (InsideZhang, 23, HeNan, InsideLi, 23, AnHui)
}
C++14/17遍历

  这里先空着///之后再补

  

参考资料

(1)《C++Primer》
(2)《快速上手C++11/14》
(3)https://blog.csdn.net/yockie/article/details/89511498

你可能感兴趣的:(C/C++)