【C++之STL】011序列容器篇list非更易型操作

c.empyt()

在C++的STL(Standard Template Library)中,std::list 是一个双向链表容器。empty() 是 std::list 的一个成员函数,用于检查链表是否为空。

empty() 函数返回一个布尔值:如果列表中没有元素,则返回 true;否则返回 false。

下面是一个简单的示例代码,展示了如何使用 empty() 函数:

#include <iostream>
#include <list>

int main() {
    std::list<int> myList;

    // 列表现在是空的,empty() 应该返回 true
    if (myList.empty()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    // 向列表中添加元素
    myList.push_back(1);
    myList.push_back(2);
    myList.push_back(3);

    // 列表现在不是空的,empty() 应该返回 false
    if (myList.empty()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

在这个示例中,我们创建了一个 std::list 类型的 myList。在添加任何元素之前,myList.empty() 将返回 true,因为列表是空的。然后,我们向列表中添加了几个元素,再次调用 myList.empty() 时将返回 false,因为列表现在包含元素。

empty() 函数是常数时间复杂度的操作,即无论列表中有多少元素,它都是立即返回结果的,不需要遍历整个列表。

c.size()

在C++中,std::list 是一个双向链表容器,它允许我们在其前端和后端进行插入和删除操作。std::list 容器有一个成员函数 size(),它返回链表中元素的数量。

下面是一个简单的例子,演示了如何使用 std::list 和 size() 函数:

#include <iostream>
#include <list>

int main() {
    std::list<int> c;

    // 添加元素到列表
    c.push_back(1);
    c.push_back(2);
    c.push_back(3);
    c.push_back(4);
    c.push_back(5);

    // 获取并打印列表的大小
    std::cout << "Size of list c: " << c.size() << std::endl;

    return 0;
}

在这个例子中,我们创建了一个 std::list 类型的对象 c,并添加了五个整数元素。然后,我们调用 size() 函数来获取并打印列表的大小。输出应该是 5,因为列表中有五个元素。

c.max_szie()

在C++中,std::list的max_size成员函数返回的是该容器在理论上可能包含的最大元素数量。这个值通常是实现定义的,并且可能非常大,但是不一定表示容器在物理内存中能够容纳这么多元素。它主要用于比较不同容器类型之间的潜在容量,而不是作为容器实际大小的限制。

max_size通常用于在编程时确定一个容器是否足以容纳某个特定数量的元素,尽管在现实中很少会遇到接近这个理论上限的情况。

以下是如何使用max_size的一个简单示例:

#include <iostream>
#include <list>

int main() {
    std::list<int> c;

    // 获取并打印列表的最大可能大小
    std::size_t maxSize = c.max_size();
    std::cout << "Maximum possible size of list c: " << maxSize << std::endl;

    // 假设我们想要存储的元素数量
    std::size_t desiredSize = 1000000000; // 例如,十亿个元素

    // 检查所需的元素数量是否超过了最大可能大小
    if (desiredSize > maxSize) {
        std::cout << "Desired size exceeds the maximum possible size of the list." << std::endl;
    } else {
        std::cout << "Desired size is within the maximum possible size of the list." << std::endl;
    }

    return 0;
}

在这个例子中,我们创建了一个std::list类型的对象c,并调用了max_size来获取并打印出该列表的最大可能大小。然后,我们检查了一个假设的元素数量(在这个例子中是十亿)是否超过了这个理论上的最大值。

需要注意的是,尽管max_size提供了一个理论上的上限,但在实际使用中,尝试存储接近这个数值的元素数量可能会导致程序崩溃或性能严重下降,因为操作系统可能无法分配这么多连续的内存空间。此外,不同的编译器和操作系统可能会有不同的max_size实现,因此这个值在不同的环境中可能会有所不同。

在实际编程中,通常不需要关心max_size,除非你正在处理非常大的数据集,并且需要确保你的容器能够容纳它们。在大多数情况下,更应该关心的是程序的逻辑和性能,而不是容器的理论最大容量。

c.assign

在C++中,std::list 容器提供了 assign 成员函数,它用于给列表分配一定数量的元素,每个元素具有指定的值。assign 可以用来初始化列表,或者改变现有列表的内容。

assign 函数有几个重载版本,允许你以不同的方式指定要分配的元素数量和值。以下是 assign 的一些常见用法:

  • 分配指定数量的相同元素
  • 你可以指定一个元素值和一个数量,assign 将用指定数量的该值填充列表。
std::list<int> c;
c.assign(10, 42); // 列表 c 现在包含 10 个元素,每个元素的值都是 42

从另一个容器复制元素
你可以传递一个迭代器范围(由两个迭代器定义)给 assign,它将从该范围复制元素到列表。

std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> c;
c.assign(vec.begin(), vec.end()); // 列表 c 现在包含向量 vec 的所有元素

从初始化列表复制元素
你还可以使用花括号初始化列表的方式调用 assign。

std::list<int> c;
c.assign({1, 2, 3, 4, 5}); // 列表 c 现在包含 1, 2, 3, 4, 5

清空列表
如果你想清空列表而不改变它的大小,你可以传递 0 给 assign。

std::list<int> c = {1, 2, 3, 4, 5};
c.assign(0); // 列表 c 现在为空

在使用 assign 时,需要注意以下几点:

  • 如果列表已经包含元素,assign 会清除现有元素并用新元素替换它们。
  • assign 函数的参数类型决定了它将如何分配元素。如果传递一个整数和一个值,它会分配指定数量的该值。如果传递迭代器,它会复制迭代器范围内的元素。
  • assign 不会改变列表的大小,除非你明确指定了一个不同的数量。

下面是一个更完整的例子,展示了 assign 的不同用法:

#include <iostream>
#include <list>
#include <vector>

int main() {
    std::list<int> c;

    // 分配指定数量的相同元素
    c.assign(5, 10); // c: 10, 10, 10, 10, 10

    // 从另一个容器复制元素
    std::vector<int> vec = {1, 2, 3};
    c.assign(vec.begin(), vec.end()); // c: 1, 2, 3

    // 从初始化列表复制元素
    c.assign({4, 5, 6}); // c: 4, 5, 6

    // 清空列表
    c.assign(0); // c: (empty)

    // 再次分配指定数量的相同元素
    c.assign(2, 20); // c: 20, 20

    // 打印列表内容
    for (int x : c) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

20 20

这个例子展示了如何使用 assign 函数来填充和清空 std::list 容器。

c1.swap(c2)

在C++中,std::list 提供了一个 swap 成员函数,用于交换两个列表的内容。swap 函数接受另一个 std::list 对象作为参数,并交换两个列表的元素。这种交换操作是非常高效的,因为它通常只交换两个列表的内部指针,而不是实际移动元素。

下面是一个使用 swap 函数的示例:

#include <iostream>
#include <list>

int main() {
    std::list<int> c1 = {1, 2, 3, 4, 5};
    std::list<int> c2 = {6, 7, 8, 9, 10};

    // 打印交换前的列表
    std::cout << "List c1 before swap: ";
    for (int x : c1) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    std::cout << "List c2 before swap: ";
    for (int x : c2) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    // 使用 swap 函数交换两个列表的内容
    c1.swap(c2);

    // 打印交换后的列表
    std::cout << "List c1 after swap: ";
    for (int x : c1) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    std::cout << "List c2 after swap: ";
    for (int x : c2) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将会是:

List c1 before swap: 1 2 3 4 5 
List c2 before swap: 6 7 8 9 10 
List c1 after swap: 6 7 8 9 10 
List c2 after swap: 1 2 3 4 5

在这个例子中,c1 和 c2 是两个包含不同元素的列表。通过调用 c1.swap(c2),c1 的内容被交换到了 c2,而 c2 的内容被交换到了 c1。

swap 函数的优点在于它不会进行实际的元素复制,因此性能开销很小。这在处理大型容器时尤其有用,因为复制整个容器可能会导致大量的内存分配和复制操作。使用 swap 可以避免这些额外的开销。

请注意,swap 函数不会检查两个列表是否是同一个对象。如果 c1 和 c2 指向同一个列表,交换操作将仍然执行,导致列表的内容与其自身交换,从而不会改变列表的内容。

swap(c1,c2)

在C++中,std::swap是一个泛型算法,它可以交换两个对象的值。对于std::list(或者任何其他STL容器),你可以使用std::swap来交换两个容器的内容。然而,对于std::list,使用成员函数swap通常更加高效,因为它可以仅通过交换内部指针来实现,而不是移动整个元素。

std::swap是一个模板函数,它可以接受任何类型的两个对象,并交换它们的值。对于std::list,std::swap将交换两个列表的内容,但是相比std::list的成员函数swap,它可能会更慢,因为它通常需要通过迭代器来逐个交换元素。

下面是使用std::swap来交换两个std::list对象的示例:

#include <iostream>
#include <list>
#include <algorithm> // 为了使用std::swap

int main() {
    std::list<int> c1 = {1, 2, 3, 4, 5};
    std::list<int> c2 = {6, 7, 8, 9, 10};

    // 打印交换前的列表
    std::cout << "List c1 before swap: ";
    for (int x : c1) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    std::cout << "List c2 before swap: ";
    for (int x : c2) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    // 使用泛型std::swap来交换两个列表的内容
    std::swap(c1, c2);

    // 打印交换后的列表
    std::cout << "List c1 after swap: ";
    for (int x : c1) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    std::cout << "List c2 after swap: ";
    for (int x : c2) {
        std::cout << x << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将会是:

List c1 before swap: 1 2 3 4 5 
List c2 before swap: 6 7 8 9 10 
List c1 after swap: 6 7 8 9 10 
List c2 after swap: 1 2 3 4 5

在这个例子中,std::swap(c1, c2)调用了std::list特化的std::swap版本(如果存在的话),该版本会交换两个列表的内容。然而,请注意,即使std::swap被调用,对于std::list来说,它可能仍然会调用std::list的成员函数swap来实现交换,因为std::list的成员函数swap通常更高效。

在实践中,如果你正在处理std::list,最好使用成员函数swap来获得最佳性能。对于其他类型的容器,如std::vector,std::swap可能更加合适,因为它允许容器在不移动元素的情况下交换内容。

c.front()

在C++中,std::list是一个双向链表容器,它提供了对链表元素的直接访问。front()是std::list的一个成员函数,它返回对链表中第一个元素的引用。如果列表为空,调用front()将导致未定义行为,通常是一个程序崩溃。

以下是front()函数的一些详细用法:

访问第一个元素:
你可以使用front()来访问链表中的第一个元素。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 访问并打印第一个元素
    std::cout << "The first element is: " << myList.front() << std::endl;

    return 0;
}

输出将是:

The first element is: 1

修改第一个元素:
由于front()返回的是对第一个元素的引用,你可以用它来修改该元素的值。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 修改第一个元素的值
    myList.front() = 10;

    // 打印修改后的第一个元素
    std::cout << "The first element after modification is: " << myList.front() << std::endl;

    return 0;
}

输出将是:

The first element after modification is: 10

检查链表是否为空:
在调用front()之前,最好先检查链表是否为空。如果链表为空,调用front()将导致未定义行为。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList;

    // 检查链表是否为空
    if (!myList.empty()) {
        // 如果不为空,访问第一个元素
        std::cout << "The first element is: " << myList.front() << std::endl;
    } else {
        // 如果为空,打印相应的消息
        std::cout << "The list is empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.

front()函数对于快速访问链表的第一个元素非常有用,但记住,你必须确保在调用front()之前链表不是空的。如果你需要在不检查空的情况下安全地访问元素,可以考虑使用std::list::begin()函数,它返回一个指向第一个元素的迭代器,然后在访问该迭代器之前检查它是否等于std::list::end(),这表示链表为空。

c.back()

在C++中,std::list是一个双向链表容器,它提供了对链表元素的直接访问。back()是std::list的一个成员函数,它返回对链表中最后一个元素的引用。

back()函数通常用于访问链表的最后一个元素,而不需要遍历整个链表。这提供了非常高效的访问方式,因为它只需要访问链表的尾部。

下面是一些使用back()函数的示例:

#include <iostream>
#include <list>

int main() {
    std::list<int> myList;

    // 向链表中添加一些元素
    myList.push_back(1);
    myList.push_back(2);
    myList.push_back(3);

    // 使用back()访问并打印最后一个元素
    std::cout << "The last element is: " << myList.back() << std::endl;

    // 修改最后一个元素的值
    myList.back() = 10;

    // 再次打印最后一个元素,以验证它已被修改
    std::cout << "The last element after modification is: " << myList.back() << std::endl;

    // 尝试在空列表上调用back(),将会导致未定义行为(通常是程序崩溃)
    std::list<int> emptyList;
    // emptyList.back(); // 这行代码将导致错误

    return 0;
}

输出将会是:

The last element is: 3
The last element after modification is: 10

请注意,如果链表是空的,调用back()函数将导致未定义行为,通常是程序崩溃。因此,在调用back()之前,最好检查链表是否为空,可以使用empty()成员函数来实现这一点:

if (!myList.empty()) {
    // 链表不为空,可以安全地调用back()
    std::cout << "The last element is: " << myList.back() << std::endl;
} else {
    std::cout << "The list is empty." << std::endl;
}

在上面的代码中,我们首先检查myList是否为空,如果不为空,则安全地调用back()。这样可以避免在空链表上调用back()导致的错误。

c.begin()

在C++中,std::list 是一个双向链表容器,它提供了对链表元素的直接访问。begin() 是 std::list 的一个成员函数,它返回一个指向链表中第一个元素的迭代器。如果链表是空的,begin() 返回的迭代器将等于 end() 返回的迭代器。

下面是 begin() 的一些详细用法:
访问第一个元素

你可以使用 begin() 返回的迭代器来访问链表中的第一个元素。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用 begin() 访问第一个元素
    std::list<int>::iterator it = myList.begin();
    std::cout << "The first element is: " << *it << std::endl;

    return 0;
}

输出将是:

The first element is: 1

遍历链表

你可以使用 begin() 和 end() 来遍历整个链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用 begin() 和 end() 遍历链表
    for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5

使用范围基础的 for 循环

C++11 引入了范围基础的 for 循环(也称为基于范围的 for 循环),这使得遍历容器更加简洁。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用范围基础的 for 循环遍历链表
    for (const auto& element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5

检查链表是否为空

在遍历链表之前,通常检查链表是否为空是一个好习惯。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.begin() == myList.end()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        // 遍历链表
        for (const auto& element : myList) {
            std::cout << element << ' ';
        }
        std::cout << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.

begin() 是一个非常有用的函数,它允许你以迭代器的形式访问链表中的元素,从而能够遍历、修改或检查链表的内容。

c.end()

在C++中,std::list的end()成员函数返回一个迭代器,该迭代器指向链表的“尾部之后”的位置。需要注意的是,这个迭代器并不指向链表的最后一个元素,而是指向最后一个元素之后的位置。因此,它不能被解引用。end()通常用于循环中,作为迭代器的终止条件。

以下是end()函数的一些详细用法:

  1. 作为循环终止条件

当遍历std::list时,end()函数常常用作循环的终止条件。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用end()作为循环终止条件
    for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5
  1. 检查链表是否为空

begin()和end()也可以用来检查链表是否为空。如果begin()返回的迭代器等于end()返回的迭代器,那么链表就是空的。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.begin() == myList.end()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.
  1. 与rbegin()和rend()的区别

end()与rbegin()和rend()不同。rbegin()返回一个指向链表最后一个元素的反向迭代器,而rend()返回一个指向链表第一个元素之前的反向迭代器。它们用于反向遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用rbegin()和rend()反向遍历链表
    for (auto it = myList.rbegin(); it != myList.rend(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

5 4 3 2 1

总之,end()函数在std::list中非常有用,它允许你安全地遍历链表,直到达到链表的末尾。然而,记住不要尝试解引用end()返回的迭代器,因为它不指向任何有效的元素。

c.cbegin()

在C++中,std::list的cbegin()成员函数返回一个指向链表中第一个元素的常量迭代器。与begin()函数类似,cbegin()也用于访问链表的开始位置,但它返回的迭代器是常量的,这意味着你不能通过这个迭代器来修改链表中的元素。

使用cbegin()的好处是,当你不打算修改链表中的元素时,它可以明确地表明你的意图,并可能提供编译时的类型安全检查。此外,如果std::list对象是一个常量对象或者一个即将被销毁的对象(比如在std::unique_ptr中),cbegin()是唯一可以安全使用的迭代器。

下面是一些cbegin()的详细用法示例:

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用cbegin()访问链表的第一个元素
    auto it = myList.cbegin();

    // 可以读取元素,但不能修改它们
    std::cout << "The first element is: " << *it << std::endl;

    // 尝试通过cbegin()返回的迭代器修改元素将导致编译错误
    // *it = 10; // 错误:'std::list<int>::const_iterator' 不支持修改操作

    // 使用范围基础的for循环和cbegin()遍历链表
    for (const auto& element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    // 对于常量链表对象,只能使用cbegin()和cend()
    const std::list<int> constList = {1, 2, 3};
    for (auto it = constList.cbegin(); it != constList.cend(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

The first element is: 1
1 2 3 4 5
1 2 3

请注意,如果你尝试通过cbegin()返回的迭代器来修改元素,编译器将报错,因为该迭代器是常量迭代器。

此外,当处理可能为空的链表时,你应该始终检查cbegin()和cend()之间的迭代器是否有效,以避免未定义行为。对于cbegin(),这通常意味着检查它是否等于cend(),如果是,则表示链表为空。

c.cend()

在C++中,std::list的cend()成员函数返回一个常量迭代器,该迭代器指向链表中“尾部之后”的位置。与end()函数类似,cend()也用于表示链表的结束位置,但它返回的迭代器不能被用来修改链表中的元素,因为它是常量迭代器。

cend()常用于在基于范围的for循环中遍历链表,因为它确保了循环内部不能修改链表元素。此外,当处理可能为空或者即将被销毁的链表时,使用cend()作为循环终止条件也是安全的,因为它不会解引用一个无效的迭代器。

下面是一些cend()的详细用法示例:

  1. 基于范围的for循环中使用cend()
#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用cend()在基于范围的for循环中遍历链表
    for (const auto& element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5
  1. 与cbegin()和end()一起使用

虽然cend()通常与基于范围的for循环一起使用,但你也可以显式地使用它与cbegin()来遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用cbegin()和cend()遍历链表
    for (auto it = myList.cbegin(); it != myList.cend(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将与前面的示例相同。
3. 检查链表是否为空

cend()也可以用于检查链表是否为空,就像end()一样。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.cbegin() == myList.cend()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.
  1. 与rbegin()和crend()的区别

cend()与rbegin()和crend()不同。rbegin()返回一个指向链表最后一个元素的反向迭代器,而crend()返回一个指向链表第一个元素之前的反向迭代器的常量版本。它们用于反向遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用rbegin()和crend()反向遍历链表
    for (auto it = myList.rbegin(); it != myList.crend(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

5 4 3 2 1

总的来说,cend()是一个非常有用的成员函数,它允许你安全地遍历链表而不用担心修改链表中的元素。在处理常量链表或者需要确保不修改链表内容的场景中,应该优先使用cbegin()和cend()。

c.rbegin()

在C++中,std::list的rbegin()成员函数返回一个反向迭代器,该迭代器指向链表的最后一个元素。与begin()不同,rbegin()允许你从链表的尾部开始遍历,向头部方向前进。这在你需要从后向前处理链表元素时非常有用。

rbegin()返回的迭代器是反向迭代器,因此它的行为与普通迭代器有所不同。你不能直接将其转换为普通迭代器,反之亦然。此外,反向迭代器的++和–操作符的行为与普通迭代器相反:++会使迭代器向前移动(即向头部方向移动),而–会使迭代器向后移动(即向尾部方向移动)。

以下是rbegin()函数的一些详细用法示例:

  1. 反向遍历链表

你可以使用rbegin()和rend()(返回指向链表第一个元素之前的反向迭代器)来反向遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用rbegin()和rend()反向遍历链表
    for (auto rit = myList.rbegin(); rit != myList.rend(); ++rit) {
        std::cout << *rit << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

5 4 3 2 1
  1. 结合C++11的范围基础for循环使用

在C++11及更高版本中,你可以使用范围基础for循环来简化反向遍历。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用范围基础for循环反向遍历链表
    for (auto element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    // 使用范围基础for循环和rbegin()反向遍历链表
    for (auto element : myList | std::reverse_iterator<decltype(myList.begin())>) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5
5 4 3 2 1

在第二个范围基础for循环中,我们使用了std::reverse_iterator来将普通迭代器转换为反向迭代器。
3. 检查链表是否为空

你也可以使用rbegin()和rend()来检查链表是否为空。如果rbegin()等于rend(),则链表为空。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.rbegin() == myList.rend()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.

在使用rbegin()时,请注意它是返回一个指向链表尾部的反向迭代器,而不是一个指向链表头部的普通迭代器。因此,它通常与rend()一起使用,以正确地遍历或检查链表。

c.rend()

在C++中,std::list的rend()成员函数返回一个指向链表中“第一个元素之前”的位置的反向迭代器。换句话说,它返回一个反向迭代器,该迭代器指向链表的尾部之后的位置。这个迭代器不能被解引用,因为它不指向链表中的任何实际元素。它主要用于作为反向遍历链表的终止条件。

rend()常用于与rbegin()一起使用,在反向遍历链表时提供结束条件。这与end()和begin()在正向遍历链表时的用法类似。

以下是rend()的一些详细用法示例:

  1. 反向遍历链表

你可以使用rbegin()和rend()来反向遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用rbegin()和rend()反向遍历链表
    for (auto rit = myList.rbegin(); rit != myList.rend(); ++rit) {
        std::cout << *rit << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

5 4 3 2 1
  1. 检查链表是否为空

rend()也可以用于检查链表是否为空。如果rbegin()等于rend(),则链表为空。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.rbegin() == myList.rend()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.
  1. 与crbegin()和crend()的区别

值得注意的是,rend()与crbegin()和crend()不同。crbegin()返回一个指向链表中第一个元素的常量反向迭代器,而crend()返回一个指向链表尾部之后的常量反向迭代器的位置。它们用于在不能修改链表内容的情况下反向遍历链表。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用crbegin()和crend()反向遍历链表(常量方式)
    for (const auto& rit = myList.crbegin(); rit != myList.crend(); ++rit) {
        std::cout << *rit << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将与前面的示例相同。

总的来说,rend()是std::list的一个非常有用的成员函数,它提供了反向遍历链表的终止条件。当你需要遍历链表但从尾部开始时,或者需要检查链表是否为空时,可以使用rend()。与cend()类似,rend()返回的迭代器是常量迭代器,不能被用来修改链表中的元素。

c.crbegin()

在C++中,std::list的crbegin()成员函数返回一个常量反向迭代器,该迭代器指向链表的最后一个元素。与rbegin()类似,crbegin()允许你从链表的尾部开始反向遍历链表,但你不能使用它来修改链表中的元素,因为它是一个常量反向迭代器。

crbegin()非常有用,当你需要从后向前处理链表元素,但又不想(或不能)修改这些元素时。它返回的迭代器指向链表的最后一个元素,使得你可以使用反向迭代来遍历链表。

下面是一些使用crbegin()的示例:

  1. 反向遍历链表(只读)
#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用crbegin()和crend()反向遍历链表(只读)
    for (const auto& element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    // 或者使用显式的常量反向迭代器
    for (const auto& rit = myList.crbegin(); rit != myList.crend(); ++rit) {
        std::cout << *rit << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5
1 2 3 4 5

注意,在这个例子中,我们使用了基于范围的for循环,但它实际上内部使用了begin()和end(),而不是crbegin()和crend()。如果你想要明确表示你正在使用常量迭代器,你应该使用第二种方式,即显式地使用crbegin()和crend()。
2. 结合C++11的范围基础for循环使用

在C++11及更高版本中,你也可以使用范围基础for循环来简化反向遍历,但请注意,这种方式并不显式地使用crbegin()和crend()。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用范围基础for循环反向遍历链表(只读)
    for (const auto& element : myList | std::reverse_iterator<decltype(myList.begin())>) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出与前面的例子相同。

  1. 检查链表是否为空

你也可以使用crbegin()和crend()来检查链表是否为空。如果crbegin()等于crend(),则链表为空。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.crbegin() == myList.crend()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.

总的来说,crbegin()是std::list的一个非常有用的成员函数,它允许你以只读的方式从链表的尾部开始反向遍历。在需要反向处理链表数据但又不想或不能修改数据时,它是非常合适的。

c.crend()

在C++中,std::list的crend()成员函数返回一个常量反向迭代器,该迭代器指向链表的“最后一个元素之后”的位置。换句话说,它指向链表的尾部之后的位置,因此不能被解引用。crend()通常用于作为反向遍历链表的终止条件,与crbegin()一起使用。

crend()返回的迭代器是常量反向迭代器,这意味着你不能通过它来修改链表中的元素。它主要用于在需要读取链表元素但不能修改它们时进行反向遍历。

以下是一些crend()的详细用法示例:

  1. 反向遍历链表(只读)

你可以使用crbegin()和crend()来反向遍历链表,同时确保不会修改链表中的元素。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用crbegin()和crend()反向遍历链表(只读)
    for (const auto& element : myList) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    // 或者使用显式的常量反向迭代器
    for (const auto& rit = myList.crbegin(); rit != myList.crend(); ++rit) {
        std::cout << *rit << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出将是:

1 2 3 4 5
1 2 3 4 5

在这个例子中,我们使用了基于范围的for循环,它内部实际上使用了crbegin()和crend()。如果你想要显式地使用常量反向迭代器,你可以像第二个循环那样做。
2. 检查链表是否为空

你还可以使用crbegin()和crend()来检查链表是否为空。如果crbegin()等于crend(),则链表为空。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList; // 空链表

    // 检查链表是否为空
    if (myList.crbegin() == myList.crend()) {
        std::cout << "The list is empty." << std::endl;
    } else {
        std::cout << "The list is not empty." << std::endl;
    }

    return 0;
}

输出将是:

The list is empty.
  1. 结合C++11的范围基础for循环使用

在C++11及更高版本中,你也可以使用范围基础for循环来简化反向遍历,但请注意,这种方式并不显式地使用crbegin()和crend()。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用范围基础for循环反向遍历链表(只读)
    for (const auto& element : myList | std::reverse_iterator<decltype(myList.begin())>) {
        std::cout << element << ' ';
    }
    std::cout << std::endl;

    return 0;
}

输出与前面的例子相同。

总的来说,crend()是std::list的一个非常有用的成员函数,它返回一个指向链表尾部之后位置的常量反向迭代器。它主要用于反向遍历链表的终止条件,确保在遍历过程中不会修改链表中的元素。

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

熊猫Devin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值