C++/C++11中std::stack的使用

栈stack 是一个容器适配器(container adaptor)类型,被特别设计用来运行于LIFO(Last-in First-out,后进先出)场景,在该场景中,只能从容器末尾添加和删除元素,其定义在stack头文件中。stack默认基于std::deque实现,也可以在std::list或std::vector之上实现。

stack 通常被实现为容器适配器,即使用一个特定容器类的封装对象作为它的底层容器。stack 提供了一系列成员函数用于操作它的元素,只能从容器”后面”压进(Push)元素或从容器”后面”提取(Pop)元素。容器中的”后面”位置也被称为”栈顶”。用来实现栈的底层容器必须满足顺序容器的所有必要条件。同时,它还必须提供以下语义的成员函数:back()、push_back()​、pop_back()。满足上述条件的标准容器有 std::vector、std::deque 及 std::list,如果未特别指定 stack 的底层容器,标准容器 std::deque 将被使用。

     Stacks are a type of container adaptor, specifically designed to operate in a LIFO context(last-in first-out), where elements are inserted and extracted only from one end of the container.

stacks are implemented as containers adaptors, which are classes that use an encapsulated object of a specific container class as its underlying container,providing a specific set of member functions to access its elements. Elements are pushed/popped from the "back" of the specific container, which is known as the top of the stack.

一个容器就是一些特定类型对象的集合。顺序容器(sequential container)为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。

         标准库中的顺序容器包括:

         (1)、vector:可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。

         (2)、deque:双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。

         (3)、list:双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。

         (4)、forward_list:单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。

         (5)、array:固定大小数组。支持快速随机访问。不能添加或删除元素。

         (6)、string:与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。

         除了固定大小的array外,其它容器都提供高效、灵活的内存管理。我们可以添加和删除元素,扩张和收缩容器的大小。容器保存元素的策略对容器操作的效率有着固定的,有时是重大的影响。在某些情况下,存储策略还会影响特定容器是否支持特定操作。

         例如,string和vector将元素保存在连续的内存空间中。由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是,在这两种容器的中间位置添加或删除元素就会非常耗时:在一次插入或删除操作后,需要移动插入/删除位置之后的所有元素,来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。

         list和forward_list两个容器的设计目的是令容器任何位置的添加和删除操作都很快速。作为代价,这两个容器不支持元素的随机访问:为了访问一个元素,我们只能遍历整个容器。而且,与vector、deque和array相比,这两个容器的额外内存开销也很大。

         deque是一个更为复杂的数据结构。与string和vector类似,deque支持快速的随机访问。与string和vector一样,在deque的中间位置添加或删除元素的代价(可能)很高。但是,在deque的两端添加或删除元素都是很快的,与list或forward_list添加删除元素的速度相当。

         forward_list和array是新C++标准增加的类型。与内置数组相比,array是一个种更安全、更容易使用的数组类型。与内置数组类似,array对象的大小是固定的。因此,array不支持添加和删除元素以及改变容器大小的操作。forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。因此,forward_list没有size操作,因为保存或计算其大小就会比手写链表多出额外的开销。对其他容器而言,size保证是一个快速的常量时间的操作。

         通常,使用vector是最好的选择,除法你有很好的理由选择其他容器。

         以下是一些选择容器的基本原则:

         (1)、除法你有很好的理由选择其他容器,否则应该使用vector;

         (2)、如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用list或forward_list;

         (3)、如果程序要求随机访问元素,应使用vector或deque;

         (4)、如果程序要求在容器的中间插入或删除元素,应使用list或forward_list;

(5)、如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque;

(6)、如果程序只有在读取输入时才需要在容器中间位置插入元素,随后需要随机访问元素,则:首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,    通常可以很容器地向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素。如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。

如果你不确定应该使用哪种容器,那么可以在程序中只使用vector和list公共的操作:使用迭代器,不使用下标操作,避免随机访问。这样,在必要时选择使用vector或list都很方便。

一般来说,每个容器都定义在一个头文件中,文件名与类型名相同。即,deque定义在头文件deque中,list定义在头文件list中,以此类推。容器均定义为模板类。

顺序容器几乎可以保存任意类型的元素。特别是,我们可以定义一个容器,其元素的类型是另一个容器。这种容器的定义与任何其他容器类型完全一样:在尖括号中指定元素类型(此种情况下,是另一种容器类型)。

除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue和priority_queue。适配器(adaptor)是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
 

#include "stack.hpp"
#include <iostream>
#include <stack>
#include <vector>
#include <deque>
#include <list>
#include <string>
 

// https://msdn.microsoft.com/en-us/library/56fa1zk5.aspx
int test_stack_2()
{
	using namespace std;
 
	// Declares stack with default deque base container  
	stack <char> dsc1;
 
	//Explicitly declares a stack with deque base container  
	stack <char, deque<char> > dsc2;
 
	// Declares a stack with vector base containers  
	stack <int, vector<int> > vsi1;
 
	// Declares a stack with list base container  
	stack <int, list<int> > lsi;
 
	// The second member function copies elements from a container  
	vector<int> v1;
	v1.push_back(1);
	stack <int, vector<int> > vsi2(v1);
	cout << "The element at the top of stack vsi2 is "
		<< vsi2.top() << "." << endl;
 
	return 0;
}
 
///
// reference: http://www.cplusplus.com/reference/stack/stack/
int test_stack_1()
{
{ // stack::stack: Constructs a stack container adaptor object
	std::deque<int> mydeque(3, 100);          // deque with 3 elements
	std::vector<int> myvector(2, 200);        // vector with 2 elements
 
	std::stack<int> first;                    // empty stack
	std::stack<int> second(mydeque);         // stack initialized to copy of deque
 
	std::stack<int, std::vector<int> > third;  // empty stack using vector
	std::stack<int, std::vector<int> > fourth(myvector);
 
	std::cout << "size of first: " << first.size() << '\n';
	std::cout << "size of second: " << second.size() << '\n';
	std::cout << "size of third: " << third.size() << '\n';
	std::cout << "size of fourth: " << fourth.size() << '\n';
}
 
{ // stack::emplace: c++11, Adds a new element at the top of the stack, above its current top element.
  // This new element is constructed in place passing args as the arguments for its constructor
	std::stack<std::string> mystack;
 
	mystack.emplace("First sentence");
	mystack.emplace("Second sentence");
 
	std::cout << "mystack contains:\n";
	while (!mystack.empty()) {
		std::cout << mystack.top() << '\n';
		mystack.pop();
	}
}
 
{ // stack::empty: Returns whether the stack is empty: i.e. whether its size is zero
  // stack::pop: Removes the element on top of the stack, effectively reducing its size by one
  // stack::push: Inserts a new element at the top of the stack, above its current top element.
  // The content of this new element is initialized to a copy of val.
	std::stack<int> mystack;
	int sum(0);
 
	for (int i = 1; i <= 10; i++) mystack.push(i);
 
	while (!mystack.empty()) {
		sum += mystack.top();
		mystack.pop();
	}
 
	std::cout << "total: " << sum << '\n';
}
 
{ // stack::size: Returns the number of elements in the stack.
	std::stack<int> myints;
	std::cout << "0. size: " << myints.size() << '\n';
 
	for (int i = 0; i < 5; i++) myints.push(i);
	std::cout << "1. size: " << myints.size() << '\n';
 
	myints.pop();
	std::cout << "2. size: " << myints.size() << '\n';
}
 
{ // stack::swap: Exchanges the contents of the container adaptor (*this) by those of x.
	std::stack<int> foo, bar;
	foo.push(10); foo.push(20); foo.push(30);
	bar.push(111); bar.push(222);
 
	foo.swap(bar);
	// std::swap(foo, bar);
 
	std::cout << "size of foo: " << foo.size() << '\n';
	std::cout << "size of bar: " << bar.size() << '\n';
}
 
{ // stack::top: Returns a reference to the top element in the stack. 
	std::stack<int> mystack;
 
	mystack.push(10);
	mystack.push(20);
 
	mystack.top() -= 5;
 
	std::cout << "mystack.top() is now " << mystack.top() << '\n';
}
 
	return 0;
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值