与Java相比,C++ STL数据结构

tre*_*r-e 9 c++ java

我目前正在学习C++并试图习惯它附带的标准数据结构,但它们看起来都非常简单.例如,list没有像我在Java中习惯的get(index)这样的简单访问器.pop_back和pop_front等方法也不会返回列表中的对象.所以你必须做以下事情:

Object blah = myList.back();
myList.pop_back();
Run Code Online (Sandbox Code Playgroud)

而不是简单的东西: Object blah = myList.pop_back();

在Java中,几乎每个数据结构都返回对象,因此您不必进行额外的调用.为什么C++的STL容器设计如此?像Java这样的常见操作在C++中不常见吗?

编辑:对不起,我想我的问题措辞很差,以获得所有这些downvotes,但肯定有人可以编辑它.为了澄清,我想知道为什么与Java相比,这样创建STL数据结构.或者我开始使用错误的数据结构集?我的观点是,这些似乎是您可能在(在我的示例中)列表中使用的常见操作,当然每个人都不想每次都编写自己的实现.

编辑:重写问题要更清楚.

Jer*_*fin 11

很多人已经回答了你提出的具体问题,所以我会试着从更大的角度来看一下.

Java和C++之间必须存在的根本区别之一是C++主要使用值,而Java主要使用引用.

例如,如果我有类似的东西:

class X {
    // ...
};

// ...
X x;
Run Code Online (Sandbox Code Playgroud)

在Java中,x只是对类型为X的对象的引用.为了引用它的类型的实际对象X,我通常有类似的东西:X x = new X;.但是,在C++中,X x;它本身定义了一个类型的对象X,而不仅仅是对象的引用.我们可以直接使用该对象,而不是通过引用(即伪装的指针).

虽然这最初看起来似乎是一个相当微不足道的差异,但影响是巨大的和普遍的.一种效果(在这种情况下可能是最重要的)是在Java中,返回值根本涉及复制对象本身.它只涉及复制对值的引用.这通常被认为是非常便宜的(可能更重要的是)完全安全 - 它永远不会抛出异常.

在C++中,您将直接处理值.当您返回一个对象时,您不仅仅是返回对现有对象的引用,而是返回该对象的值,通常是该对象状态的副本.当然,如果你愿意,也可以返回一个引用(或指针),但为了实现这一点,你必须明确它.

标准容器(如果有的话)更倾向于使用值而不是引用.向集合中添加值时,添加的内容是您传递的值的副本,当您返回某些内容时,您将获得容器本身值的副本.

除此之外,这意味着虽然返回值可能像Java一样便宜且安全,但它也可能很昂贵和/或抛出异常.如果程序员想要存储指针,他/她当然可以这样做 - 但语言并不像Java那样需要它.由于返回一个对象可能很昂贵和/或抛出,标准库中的容器通常是围绕确保它们可以合理地工作,如果复制是昂贵的,并且(更重要的是)正常工作,即使在/如果复制引发异常时也是如此.

这种设计的基本区别不仅在于您所指出的差异,还包括其他一些差异.


Yak*_*ont 5

back()返回对向量的最后一个元素的引用,这使得它几乎可以自由调用. pop_back()调用向量的最后一个元素的析构函数.

所以显然pop_back()不能返回对被销毁元素的引用.因此,为了使您的语法有效,pop_back()必须在元素被销毁之前返回该元素的副本.

现在,在你不想要那份副本的情况下,我们只是不必要地复制了一份.

C++标准容器的目标是为您提供几乎裸露的金属性能,包裹在漂亮,易于使用的敷料中.在大多数情况下,它们不会为了易用性而牺牲性能 - 并且pop_back()返回最后一个元素的副本将牺牲性能以便于使用.

可能有一个pop-and-get-back方法,但它会复制其他功能.在许多情况下,它的效率低于反叛.

作为一个具体的例子,

vector<foo> vec; // with some data in it
foo f = std::move( vec.back() ); // tells the compiler that the copy in vec is going away
vec.pop_back(); // removes the last element
Run Code Online (Sandbox Code Playgroud)

请注意,必须在元素被销毁之前完成移动以避免创建额外的临时副本... pop_back_and_get_value()在返回之前必须销毁元素,并且在返回之后将进行分配,这是浪费的.