当前位置: 首页 > news >正文

FAQ:Container Classes篇

1、Why should I use container classes rather than simple arrays?(为什么应该使用容器类而不是简单的数组?)

In terms of time and space, a contiguous array of any kind is just about the optimal construct for accessing a sequence of objects in memory, and if you are serious about performance in any language you will “often” use arrays.

从时间和空间的角度来看,任何类型的连续数组都是访问内存中对象序列的最佳构造,如果真的考虑任何语言的性能,你都会“经常”使用数组。

However, there are good arrays (e.g., containers with contiguous storage such as std::array and std::vector) and there are bad arrays (e.g., C [] arrays). Simple C [] arrays are evil because a C array is a very low level data structure with a vast potential for misuse and errors and in essentially all cases there are better alternatives – where “better” means easier to write, easier to read, less error prone, and as fast.

然而,有好的数组(如具有连续存储的容器,如 std::arraystd::vector),也有坏的数组(如C中的 [] 数组)。简单的C [] 数组是很邪恶的,因为C数组是一种非常低级的数据结构,非常容易误用和出现错误,本质上所有情况下都有更好的替代方案——“更好”意味着更容易编写、更容易读取、更少容易出错,并且速度更快。

The two fundamental problems with C arrays are that

  • a C array doesn’t know its own size
  • the name of a C array converts to a pointer to its first element at the slightest provocation

C 数组的两个基本问题是:

  • C数组不知道自己的大小
  • C数组的名称会转换为指向其第一个元素的指针

Consider some examples:

void f(int a[], int s)
{// do something with a; the size of a is sfor (int i = 0; i < s; ++i) a[i] = i;
}int arr1[20];
int arr2[10];void g()
{f(arr1,20);f(arr2,20);
}

The second call will scribble all over memory that doesn’t belong to arr2. Naturally, a programmer usually gets the size right, but it’s extra work and every so often someone makes a mistake. You should prefer the simpler and cleaner version using the standard library vector or array:

第二个调用会在内存中到处乱写不属于 arr2 的数据。自然地,程序员通常都能处理好大小,但这是额外的工作,而且不时会有人犯错。你应该更倾向使用标准库中的 vectorarray 来实现更简单、更简洁的版本。

void f(vector<int>& v)
{// do something with vfor (int i = 0; i < v.size(); ++i) v[i] = i;
}vector<int> v1(20);
vector<int> v2(10);void g()
{f(v1);f(v2);
}
template<size_t N> void f(array<int, N>& v)
{// do something with vfor (int i = 0; i < N; ++i) v[i] = i;
}array<int, 20> v1;
array<int, 10> v2;void g()
{f(v1);f(v2);
}

Since a C array doesn’t know its size, there can be no array assignment:

由于C语言的数组不知道其大小,因此不能对数组进行赋值:

void f(int a[], int b[], int size)
{a = b;            // not array assignmentmemcpy(a, b, size); // a = b// ...
}

Again, prefer vector or array:

// This one can result in a changing size
void g(vector<int>& a, vector<int>& b)
{a = b;  // ...
}// In this one, a and b must be the same size
template<size_t N>
void g(array<int, N>& a, array<int, N>& b)
{a = b;  // ...
}

Another advantage of vector here is that memcpy() is not going to do the right thing for elements with copy constructors, such as strings.

在这里,vector 的另一个优点是 memcpy() 不会对具有复制构造函数的元素(如字符串)进行正确的操作。

void f(string a[], string b[], int size)
{a = b;            // not array assignmentmemcpy(a,b,size); // disaster// ...
}void g(vector<string>& a, vector<string>& b)
{a = b;  // ...
}

A normal C array is of a fixed size determined at compile time (ignoring C99 VLAs, which currently have no analog in ISO C++):

普通的C数组在编译时具有固定的大小(忽略C99 VLAs,目前在ISO c++中没有类似的):

const int S = 10;void f(int s)
{int a1[s];    // errorint a2[S];    // ok// if I want to extend a2, I'll have to change to an array// allocated on free store using malloc() and use realloc()// ...
}

To contrast:
对比:

const int S = 10;void g(int s)
{vector<int> v1(s);    // okvector<int> v2(S);    // okv2.resize(v2.size()*2);// ...
}

C99 allows variable array bounds for local arrays, but those VLAs have their own problems. The way that array names “decay” into pointers is fundamental to their use in C and C++. However, array decay interacts very badly with inheritance. Consider:

C99允许本地数组有可变的数组边界,但这些VLA有自己的问题。数组将“衰变”命名为指针的方式是C和c++中使用指针的基础。然而,数组衰减与继承的交互非常糟糕。考虑:

class Base { void fct(); /* ... */ };
class Derived : Base { /* ... */ };void f(Base* p, int sz)
{for (int i=0; i<sz; ++i) p[i].fct();
}Base ab[20];
Derived ad[20];void g()
{f(ab,20);f(ad,20);    // disaster!
}

In the last call, the Derived[] is treated as a Base[] and the subscripting no longer works correctly when sizeof(Derived)!=sizeof(Base) – as will be the case in most cases of interest. If we used vectors instead, the error would be caught at compile time:

在最后一个调用中,Derived[] 被视为 Base[],当 sizeof(Derived)!=sizeof(Base) 时,下标将不再正确工作——这将是我们感兴趣的大多数情况。如果我们使用vector,错误将在编译时捕获:

void f(vector<Base>& v)
{for (int i=0; i<v.size(); ++i) v[i].fct();
}vector<Base> ab(20);
vector<Derived> ad(20);void g()
{f(ab);f(ad);    // error: cannot convert a vector<Derived> to a vector<Base>
}

We find that an astonishing number of novice programming errors in C and C++ relate to (mis)uses of C arrays. Use std::vector or std::array instead.

我们发现,C和C++中惊人数量的初学者编程错误与(错误)使用C数组有关。请使用std::vectorstd::array

Let’s assume the best case scenario: you’re an experienced C programmer, which almost by definition means you’re pretty good at working with arrays. You know you can handle the complexity; you’ve done it for years. And you’re smart — the smartest on the team — the smartest in the whole company. But even given all that, please read this entire FAQ and think very carefully about it before you go into “business as usual” mode.

让我们假设最好的情况:你是一名经验丰富的C程序员,这几乎意味着你非常擅长使用数组。你知道你可以处理复杂的问题;你已经做了很多年了。你很聪明,是团队中最聪明的,是整个公司最聪明的。但即便如此,在你进入“正常业务”模式之前,请仔细阅读整个FAQ并仔细思考。

Fundamentally it boils down to this simple fact: C++ is not C. That means (this might be painful for you!!) you’ll need to set aside some of your hard earned wisdom from your vast experience in C. The two languages simply are different. The “best” way to do something in C is not always the same as the “best” way to do it in C++. If you really want to program in C, please do yourself a favor and program in C. But if you want to be really good at C++, then learn the C++ ways of doing things. You may be a C guru, but if you’re just learning C++, you’re just learning C++ — you’re a newbie. (Ouch; I know that had to hurt. Sorry.)

从根本上说,这可以归结为一个简单的事实:C++不是C。这意味着(这对你来说可能很痛苦!!)你需要将C中的丰富经验中一些来之不易的智慧放到一边。这两种语言完全不同。用C语言做某事的“最佳”方式并不总是与用C++做某事的“最佳”方式相同。如果你真的想用C语言编程,请帮自己一个忙,用C语言编程。但是如果你想真正擅长C++,那就学习C++的做事方式。你可能是一个C大师,但如果你只是学习C++,那么你只是一个初学者。(哎哟;我知道那一定很痛苦。抱歉。)

Here’s what you need to realize about containers vs. arrays: 【以下是关于容器和数组你需要了解的:】

  1. Container classes make programmers more productive. So if you insist on using arrays while those around are willing to use container classes, you’ll probably be less productive than they are (even if you’re smarter and more experienced than they are!). 【容器类可以提高程序员的工作效率。因此,如果你坚持使用数组,而周围的人愿意使用容器类,你可能会比他们更低效(即使你比他们更聪明、更有经验!)。】
  2. Container classes let programmers write more robust code. So if you insist on using arrays while those around are willing to use container classes, your code will probably have more bugs than their code (even if you’re smarter and more experienced). 【容器类让程序员写出更健壮的代码。因此,如果你坚持使用数组,而周围的人都愿意使用容器类,那么你的代码可能会比他们的代码有更多的bug(即使你更聪明、更有经验)。】
  3. And if you’re so smart and so experienced that you can use arrays as fast and as safe as they can use container classes, someone else will probably end up maintaining your code and they’ll probably introduce bugs. Or worse, you’ll be the only one who can maintain your code so management will yank you from development and move you into a full-time maintenance role — just what you always wanted!【如果你很聪明,很有经验,你可以像他们使用容器类一样快速和安全地使用数组,其他人可能最终会维护你的代码,他们可能会引入错误。或者更糟的是,你将成为唯一一个可以维护你的代码的人,因此管理人员将你从开发岗位抽调到全职维护岗位——这正是你一直想要的!】

Here are some specific problems with arrays: 【下面是数组的一些具体问题。】
4. Subscripts don’t get checked to see if they are out of bounds. (Note that some container classes, such as std::vector, have methods to access elements with or without bounds checking on subscripts.) 【下标不会被检查是否越界。(注意,某些容器类,如 std::vector,有访问元素的方法,可以对下标进行边界检查,也可以不进行边界检查。)】
5. Arrays often require you to allocate memory from the heap (see below for examples), in which case you must manually make sure the allocation is eventually deleted (even when someone throws an exception). When you use container classes, this memory management is handled automatically, but when you use arrays, you have to manually write a bunch of code (and unfortunately that code is often subtle and tricky) to deal with this. For example, in addition to writing the code that destroys all the objects and deletes the memory, arrays often also force you you to write an extra try block with a catch clause that destroys all the objects, deletes the memory, then re-throws the exception. This is a real pain in the neck, as shown here. When using container classes, things are much easier. 【数组经常需要你从堆中分配内存(见下面的例子),在这种情况下,你必须手动确保分配最终被删除(即使有人抛出异常)。当你使用容器类时,这种内存管理是自动处理的,但当你使用数组时,你必须手动编写一堆代码(不幸的是,这些代码通常是微妙和棘手的)来处理它。例如,除了编写销毁所有对象并删除内存的代码外,数组通常还迫使你编写一个额外的 try 块,其中包含一个 catch 子句,用于销毁所有对象,删除内存,然后重新抛出异常。这是一个非常棘手的问题,如描述的这样。使用容器类时,事情会简单得多。】
6. You can’t insert an element into the middle of the array, or even add one at the end, unless you allocate the array via the heap, and even then you must allocate a new array and copy the elements.【不能在数组中间插入元素,甚至不能在末尾添加元素, 除非通过堆分配数组,但即使这样,也必须分配一个新数组并复制元素。】
7. Container classes give you the choice of passing them by reference or by value, but arrays do not give you that choice: they are always passed by reference. If you want to simulate pass-by-value with an array, you have to manually write code that explicitly copies the array’s elements (possibly allocating from the heap), along with code to clean up the copy when you’re done with it. All this is handled automatically for you if you use a container class. 【容器类可以选择按引用传递,也可以选择按值传递,但数组没有这个选择:它们总是按引用传递。如果你想模拟数组的值传递,就必须手动编写代码显式复制数组的元素(可能从堆中分配),以及在完成复制后清理副本的代码。如果您使用容器类,所有这些都会自动为您处理。】
8. If your function has a non-static local array (i.e., an “automatic” array), you cannot return that array, whereas the same is not true for objects of container classes.【如果函数有一个非静态的局部数组(即“自动”数组),则不能返回该数组,而对于容器类的对象则不是这样。】

Here are some things to think about when using containers: 【以下是使用容器时需要考虑的一些事情:】

  1. Different C++ containers have different strengths and weaknesses, but for any given job there’s usually one of them that is better — clearer, safer, easier/cheaper to maintain, and often more efficient — than an array. For instance, 【不同的C++容器有不同的优缺点,但对于任何给定的任务来说,通常有一种容器比数组更好——更清晰、更安全、更容易/更便宜维护,而且通常更高效。例如,】

    • You might consider a std::map instead of manually writing code for a lookup table. 【可以考虑使用 std::map,而不是手动编写查找表代码】
    • A std::map might also be used for a sparse array or sparse matrix. 【std::map也可以用于稀疏数组或稀疏矩阵。】
    • A std::array is the most array-like of the standard container classes, but it also offers various extra features such as bounds checking via the at() member function, automatic memory management even if someone throws an exception, ability to be passed both by reference and by value, etc. 【std::array 是最像数组的标准容器类,但它还提供了各种额外的特性,例如通过 at() 成员函数进行边界检查,即使抛出异常也能自动管理内存,能够按引用和按值传递,等等。】
    • A std::vector is the second-most array-like of the standard container classes, and offers additional extra features over std::array such as insertions/removals of elements. 【 std::vector 是标准容器类中第二大类似数组的类,它提供了比 std::array 更多的特性,例如插入/删除元素。】
    • A std::string is almost always better than an array of char (you can think of a std::string as a “container class” for the sake of this discussion). 【std::string 几乎总是比 char 数组好(为了讨论这个问题,你可以把 std::string 想象成一个“容器类”)。】
  2. Container classes aren’t best for everything, and sometimes you may need to use arrays. But that should be very rare, and if/when it happens: 【容器类并不适合所有情况,有时可能需要使用数组。但这种情况应该非常罕见,如果/当它发生时:】

    • Please design your container class’s public interface in such a way that the code that uses the container class is unaware of the fact that there is an array inside. 【请设计容器类的公共接口,令使用容器类的代码不知道其中有一个数组。】
    • The goal is to “bury” the array inside a container class. In other words, make sure there is a very small number of lines of code that directly touch the array (just your own methods of your container class) so everyone else (the users of your container class) can write code that doesn’t depend on there being an array inside your container class. 【目标是将数组“埋葬”在容器类中。换句话说,确保有非常少的代码行直接接触数组(只是容器类的自己的方法),以便其他人(容器类的用户)可以编写不依赖于容器类中数组的代码。】

To net this out, arrays really are evil. You may not think so if you’re new to C++. But after you write a big pile of code that uses arrays (especially if you make your code leak-proof and exception-safe), you’ll learn — the hard way. Or you’ll learn the easy way by believing those who’ve already done things like that. The choice is yours.

为了说明这一点,数组真的很邪恶。如果你刚接触C++,可能不会这么想。但是,当你编写了一大堆使用数组的代码(特别是在你的代码防泄漏和异常安全的情况下)后,你将了解这一点——这是一种艰难的方式。或者你可以通过相信那些已经做过类似事情的人来学会简单的方法。选择权在你。

2、How can I make a perl-like associative array in C++? (如何在C++中创建类似perl的关联数组?)

Use the standard class template std::map<Key,Val>:

#include <string>
#include <map>
#include <iostream>int main()
{// age is a map from string to intstd::map<std::string, int, std::less<std::string>>  age;age["Fred"] = 42;                     // Fred is 42 years oldage["Barney"] = 37;                   // Barney is 37if (todayIsFredsBirthday())           // On Fred's birthday...++ age["Fred"];                     // ...increment Fred's agestd::cout << "Fred is " << age["Fred"] << " years old\n";// ...
}

Nit: the order of elements in a std::map<Key,Val> are in the sort order based on the key, so from a strict standpoint, that is different from Perl’s associative arrays which are unordered. If you want an unsorted version for a closer match, you can use std::unordered_map<Key,Val> instead.

std::map<Key,Val> 中元素的顺序是基于键的排序顺序,所以从严格的角度来看,这与Perl的无序关联数组是不同的。如果你想要一个未排序的版本来进行更紧密的匹配,可以使用 std::unordered_map<Key,Val>

3、Is the storage for a std::vector or a std::array<T,N> guaranteed to be contiguous?(std::vectorstd::array<T,N>的存储空间保证是连续的吗?)

Yes.
当然。

This means the following technique is safe:

#include <vector>
#include <array>
#include "Foo.h"  /* get class Foo */// old-style code that wants an array
void f(Foo* array, unsigned numFoos);void g()
{std::vector<Foo> v;std::array<Foo, 10> a;// ...f(v.data(), v.size());  // Safef(a.data(), a.size());  // Safe
}

In general, it means you are guaranteed that &v[0] + n == &v[n], where v is a non-empty std::vector<T> or std::array<T,N> and n is an integer in the range 0 .. v.size()-1.

一般来说,这意味着保证 &v[0] + n == &v[n],其中 v 是非空的std::vector<T>std::array<T,N>n 是范围为0 ~ v.size() - 1 的数。

However v.begin() is not guaranteed to be a T*, which means v.begin() is not guaranteed to be the same as &v[0]:

但是 v.begin() 不保证是 T*,这意味着 v.begin() 不保证与 &v[0] 相同:

void g()
{std::vector<Foo> v;// ...f(v.begin(), v.size());  // error, not guaranteed to be the same as &v[0]↑↑↑↑↑↑↑↑↑ // cough, choke, gag; use v.data() instead
}

Also, using &v[0] is undefined behavior if the std::vector or std::array is empty, while it is always safe to use the .data() function.

另外,如果 std::vectorstd::array为空,那么使用 ·&v[0]· 就是未定义行为,而使用 .data() 函数总是安全的。

Note: It’s possible the above code might compile for you today. If your compiler vendor happens to implement std::vector or std::array iterators as T*’s, the above may happen to work on that compiler – and at that, possibly only in release builds, because vendors often supply debug iterators that carry more information than a T*. But even if this code happens to compile for you today, it’s only by chance because of a particular implementation. It’s not portable C++ code. Use .data() for these situations.

注意:上面的代码可能今天就能编译成功。如果你的编译器厂商碰巧将 std::vectorstd::array 迭代器实现为 T* 的迭代器,那么上述代码可能在该编译器上有效——而且可能只在发布版本中有效,因为厂商提供的调试迭代器通常携带的信息比 T* 更多。但即使这段代码今天能通过编译,这也只是偶然的,因为有特定的实现。它不是可移植的C++代码。在这些情况下使用 .data()

4、Why doesn’t C++ provide heterogeneous containers?(为什么C++不提供异构容器?)

The C++ standard library provides a set of useful, statically type-safe, and efficient containers. Examples are vector, list, and map:

C++ 标准库提供了一组有用的、静态类型安全的、高效的容器。例如 vectorlistmap

vector<int> vi(10);
vector<Shape*> vs;
list<string> lst;
list<double> l2
map<string,Record*> tbl;
unordered_map<Key,vector<Record*> > t2;

These containers are described in all good C++ textbooks, and should be preferred over arrays and “home cooked” containers unless there is a good reason not to.

这些容器在所有优秀的C++教科书中都有介绍,应该优先使用,而不是数组和“家常”容器,除非有充分的理由不使用。

These containers are homogeneous; that is, they hold elements of the same type. If you want a container to hold elements of several different types, you must express that either as a union or (usually much better) as a container of pointers to a polymorphic type. The classical example is:

这些容器是同构的;也就是说,它们持有相同类型的元素。如果想在容器中保存几种不同类型的元素,则必须表示为联合,或者(通常更好)表示为指向多态类型的指针的容器。经典的例子是:

vector<Shape*> vi;  // vector of pointers to Shapes

Here, vi can hold elements of any type derived from Shape. That is, vi is homogeneous in that all its elements are Shapes (to be precise, pointers to Shapes) and heterogeneous in the sense that vi can hold elements of a wide variety of Shapes, such as Circles, Triangles, etc.

在这里,vi 可以保存继承自 Shape 的任何类型的元素。也就是说,vi 是同构的,因为它的所有元素都是Shape(准确地说,是指向Shape 的指针);在某种意义上,也可以说 vi 是异构的,因为 vi 可以包含各种形状的元素,如圆、三角形等。

So, in a sense all containers (in every language) are homogeneous because to use them there must be a common interface to all elements for users to rely on. Languages that provide containers deemed heterogeneous simply provide containers of elements that all provide a standard interface. For example, Java collections provide containers of (references to) Objects and you use the (common) Object interface to discover the real type of an element.

因此,从某种意义上说,所有容器(每种语言中的容器)都是同构的,因为要使用它们,必须为用户依赖的所有元素提供一个公共接口。提供容器的语言被认为是异构的,只是提供了元素的容器,这些元素都提供了标准接口。例如,Java集合提供了对象的容器(引用),你可以使用(公共)Object接口来发现元素的真正类型。

The C++ standard library provides homogeneous containers because those are the easiest to use in the vast majority of cases, gives the best compile-time error message, and imposes no unnecessary run-time overheads.

C++标准库提供了同构的容器,因为这些容器在大多数情况下都是最容易使用的,提供了最好的编译时错误消息,并且没有不必要的运行时开销。

If you need a heterogeneous container in C++, define a common interface for all the elements and make a container of those. For example:

如果你需要在C++中使用一个异构容器,可以为所有元素定义一个公共接口,并将这些元素制成一个容器。例如:

class Io_obj { /* ... */ };    // the interface needed to take part in object I/Ovector<Io_obj*> vio;           // if you want to manage the pointers directly
vector<shared_ptr<Io_obj>> v2; // if you want a "smart pointer" to handle the objects

Don’t drop to the lowest level of implementation detail unless you have to:

除非迫不得已,否则不要把实现细节降到最低:

vector<void*> memory;          // rarely needed

A good indication that you have “gone too low level” is that your code gets littered with casts.

如果你的代码被强制转换弄得一团糟,就说明你的代码级别太低了。

Using an Any class, such as boost::any, can be an alternative in some programs:

在某些程序中,使用Any类(如boost:: Any)是一种替代方案:

vector<any> v = { 5, "xyzzy", 3.14159 };

If all objects you want to store in a container are publicly derived from a common base class, you can then declare/define your container to hold pointers to the base class. You indirectly store a derived class object in a container by storing the object’s address as an element in the container. You can then access objects in the container indirectly through the pointers (enjoying polymorphic behavior). If you need to know the exact type of the object in the container you can use dynamic_cast<> or typeid(). You’ll probably need the Virtual Constructor Idiom to copy a container of disparate object types. The downside of this approach is that it makes memory management a little more problematic (who “owns” the pointed-to objects? if you delete these pointed-to objects when you destroy the container, how can you guarantee that no one else has a copy of one of these pointers? if you don’t delete these pointed-to objects when you destroy the container, how can you be sure that someone else will eventually do the deleteing?). It also makes copying the container more complex (may actually break the container’s copying functions since you don’t want to copy the pointers, at least not when the container “owns” the pointed-to objects). In that case, you can use std::shared_ptr to manage the objects, and the containers will copy correctly.

如果你想存储在容器中的所有对象都是公共地派生自一个共同的基类,你可以声明/定义容器来保存指向基类的指针。通过将派生类对象的地址作为元素存储在容器中,可以间接地将该类对象存储在容器中。然后可以通过指针间接地访问容器中的对象(享受多态行为)。如果你想知道容器中对象的的确切类型,可以使用dynamic_cast<>typeid()。你可能需要虚构造函数惯用法来赋值不同对象类型的容器。这种方法的缺点是它使得内存管理更有问题(谁“拥有”指向的对象?如果在销毁容器的同时删除了这些指针指向的对象,如何保证这些指针中的任何一个都没有被其他人拥有副本呢?如果在销毁容器时不删除这些指向对象,又怎么能确定最终会有其他人来删除呢?)它还会使复制容器变得更加复杂(实际上可能会破坏容器的复制函数,因为你不想复制指针,至少在容器“拥有”指向的对象时不会)在这种情况下,可以使用 std::shared_ptr 来管理对象,容器将能够正确地进行复制。

The second case occurs when the object types are disjoint — they do not share a common base class. The approach here is to use a handle class. The container is a container of handle objects (by value or by pointer, your choice; by value is easier). Each handle object knows how to “hold on to” (i.e., maintain a pointer to) one of the objects you want to put in the container. You can use either a single handle class with several different types of pointers as instance data, or a hierarchy of handle classes that shadow the various types you wish to contain (requires the container be of handle base class pointers). The downside of this approach is that it opens up the handle class(es) to maintenance every time you change the set of types that can be contained. The benefit is that you can use the handle class(es) to encapsulate most of the ugliness of memory management and object lifetime.

第二种情况发生在对象类型不相交的时候——它们不共享一个共同的基类。这里的方法是使用一个handle类。容器是handle对象的容器(由值或指针决定;按值更简单)。每个handle对象都知道如何“保持”(即维护一个指向要放入容器中的对象的指针)。可以使用具有几种不同类型指针的单个handle类作为实例数据,也可以使用handle类的层次结构来遮蔽您希望包含的各种类型(要求容器为handle基类指针)。这种方法的缺点是,每次更改可以包含的类型集时,它都会打开handle类来维护。这样做的好处是,你可以使用handle类来封装内存管理和对象生存期的大部分麻烦。

5、How can I build a heterogeneous <favorite container> of objects of different types? (如何构建不同类型对象的异构<最爱容器>?)

See heterogeneous containers.

6、Why can’t I assign a vector<Apple*> to a vector<Fruit*>?(为什么不能将vector<Apple*>赋值给vector<Fruit*>?)

Because that would open a hole in the type system. For example:

因为这会在类型系统中打开一个漏洞。例如:

class Apple : public Fruit { void apple_fct(); /* ... */ };
class Orange : public Fruit { /* ... */ }; // Orange doesn't have apple_fct()vector<Apple*> v;             // vector of Applesvoid f(vector<Fruit*>& vf)    // innocent Fruit manipulating function
{vf.push_back(new Orange); // add orange to vector of fruit
}void h()
{f(v);    // error: cannot pass a vector<Apple*> as a vector<Fruit*>for (int i=0; i<v.size(); ++i) v[i]->apple_fct();
}

Had the call f(v) been legal, we would have had an Orange pretending to be an Apple.

如果调用 f(v) 合法,我们就会有一个橙子假装成苹果。

An alternative language design decision would have been to allow the unsafe conversion, but rely on dynamic checking. That would have required a run-time check for each access to v’s members, and h() would have had to throw an exception upon encountering the last element of v.

另一种语言设计决策是允许不安全的转换,但依赖动态检查。这就需要每次访问 v 的成员时都进行运行时检查,而 h() 遇到 v 的最后一个元素时必须抛出异常。

7、How can I insert/access/change elements from a linked list/hashtable/etc?(链表/哈希表等中如何插入/访问/更改元素?)

The most important thing to remember is this: don’t roll your own from scratch unless there is a compelling reason to do so. In other words, instead of creating your own list or hashtable, use one of the standard class templates such as std::vector<T> or std::list<T> or whatever.

要记住的最重要的事情是:不要自己动手,除非有令人信服的理由。换句话说,使用 std::vector<T>std::list<T> 之类的标准类模板,而不是创建自己的链表或哈希表。

Assuming you have a compelling reason to build your own container, here’s how to handle inserting (or accessing, changing, etc.) the elements.

假设你有充分的理由构建自己的容器,下面是如何处理插入(或访问、更改等)元素的方法。

To make the discussion concrete, I’ll discuss how to insert an element into a linked list. This example is just complex enough that it generalizes pretty well to things like vectors, hash tables, binary trees, etc.

为了使讨论具体化,我将讨论如何向链表中插入元素。这个例子足够复杂,可以很好地推广到向量、散列表、二叉树等。

A linked list makes it easy to insert an element before the first or after the last element of the list, but limiting ourselves to these would produce a library that is too weak (a weak library is almost worse than no library). This answer will be a lot to swallow for novice C++’ers, so I’ll give a couple of options. The first option is easiest; the second and third are better.

在链表中,可以很容易地在第一个元素之前或最后一个元素之后插入元素,但只使用这些会让库过于弱(弱的库几乎比没有库更糟糕)。这个答案对于C++新手来说很难理解,所以我将给出几个选择。第一个选择是最简单的;第二个和第三个比较好。

  1. Empower the List with a “current location,” and member functions such as advance(), backup(), atEnd(), atBegin(), getCurrElem(), setCurrElem(Elem), insertElem(Elem), and removeElem(). Although this works in small examples, the notion of a current position makes it difficult to access elements at two or more positions within the list (e.g., “for all pairs x,y do the following…”). 【赋予List一个“当前位置”和成员函数,如advance()、backup()、atEnd()、atBegin()、getCurrElem()、setCurrElem(Elem)、insertElem(Elem)和removeElem()。虽然这在小示例中有效,但当前位置的概念使得访问列表中两个或多个位置的元素变得困难(例如,“对于所有对x,y执行以下…”)。】
  2. Remove the above member functions from List itself, and move them to a separate class, ListPosition. ListPosition would act as a “current position” within a list. This allows multiple positions within the same list. ListPosition would be a friend of class List, so List can hide its innards from the outside world (else the innards of List would have to be publicized via public member functions in List). Note: ListPosition can use operator overloading for things like advance() and backup(), since operator overloading is syntactic sugar for normal member functions.【从List中删除上述成员函数,并将它们移动到单独的类ListPosition中。ListPosition相当于列表中的“当前位置”。这允许在同一个列表中有多个位置。ListPosition是List类的友元,因此List可以向外界隐藏它的内部结构(否则List的内部结构必须通过List中的public成员函数公开)。注意:ListPosition可以对advance()和backup()这样的函数使用运算符重载,因为运算符重载是普通成员函数的语法糖。】
  3. Consider the entire iteration as an atomic event, and create a class template that embodies this event. This enhances performance by allowing the public access member functions (which may be virtual functions) to be avoided during the access, and this access often occurs within an inner loop. Unfortunately the class template will increase the size of your object code, since templates gain speed by duplicating code. For more, see [Koenig, “Templates as interfaces,” JOOP, 4, 5 (Sept 91)], and [Stroustrup, “The C++ Programming Language Third Edition,” under “Comparator”]. 【将整个迭代视为原子事件,并创建包含此事件的类模板。这通过在访问期间避免公共访问成员函数(可能是虚函数)来提高性能,而且这种访问通常发生在内部循环中。不幸的是,类模板会增加目标代码的大小,因为模板通过复制代码来提高速度。更多信息,请参阅[Koenig,“Templates as interfaces”,JOOP, 4,5(9月91)]和[Stroustrup,“The c++ Programming Language Third Edition”,在“Comparator”下]。】

8、Can I have a container of smart pointers to my objects?(我可以有一个指向我的对象的智能指针的容器吗?)

Yes, and you really want to do this, as smart pointers make your life easier and make your code more robust compared to the alternatives.

是的,你确实想这样做,因为与替代方案相比,智能指针使你的生活更轻松,使你的代码更健壮。

Note: forget that std::auto_ptr ever existed. Really. You don’t want to use it, ever, especially in containers. It is broken in too many ways.

注意:请忘记 std::auto_ptr 曾经存在过。真的。你永远都不想使用它,尤其是在容器中。它在很多方面都有缺陷。

Let’s motivate this discussion with an example. This first section shows why you’d want to use smart pointers in the first place - this is what not to do:

让我们用一个例子来激发这个讨论。第一节展示了为什么你需要首先使用智能指针——这是应该做的:

#include <vector>class Foo {
public:// ...blah blah...
};void foo(std::vector<Foo*>& v)  // ← BAD FORM: a vector of dumb pointers to Foo objects
{v.push_back(new Foo());// ...delete v.back();  // you have a leak if this line is skippedv.pop_back();     // you have a "dangling pointer" if control-flow doesn't reach this line
}

If control flow doesn’t reach either of the last two lines, either because you don’t have it in your code or you do a return or something throws an exception, you will have a leak or a “dangling pointer”; bad news. The destructor of std::vector cleans up whatever allocations were made by the std::vector object itself, but it will not clean up the allocation that you made when you allocated a Foo object, even though you put a pointer to that allocated Foo object into the std::vector object.

如果控制流没有到达最后两行,要么是因为你的代码中没有它,要么是因为执行了return或抛出了异常,那么就会出现泄漏或“悬空指针”;坏消息。std::vector的析构函数会清除 std::vector 对象本身所进行的任何分配,但是它不会清除为 Foo 对象分配内存时所进行的分配,即使你将指向已分配内存的 Foo 对象的指针放在了 std::vector 对象中。

That’s why you’d want to use a smart pointer.

这就是为什么你想用智能指针.

Now let’s talk about how to use a smart pointer. There are lots of smart pointers that can be copied and still maintain shared ownership semantics, such as std::shared_ptr and many others. For this example, we will use std::shared_ptr, though you might choose another based on the semantics and performance trade-offs you desire.

现在让我们谈谈如何使用智能指针。有很多智能指针可以在被复制的同时仍然保持共享的所有权语义,比如 std::shared_ptr 等。在这个例子中,我们将使用 std::shared_ptr,不过你也可以根据语义和性能权衡选择另一种。

typedef std::shared_ptr<Foo> FooPtr;  // ← GOOD: using a smart-pointervoid foo(std::vector<FooPtr>& v)  // ← GOOD: using a container of smart-pointer
{// ...
}

This just works safely with all operations. The object is destroyed when the last shared_ptr to it is destroyed or set to point to something else.

这对所有操作都是安全的。当最后一个指向它的 shared_ptr 被销毁或被设置为指向其他对象时,对象就会被销毁。

Using a std::unique_ptr in a std::vector is safe, but it has some restrictions. The unique_ptr is a move-only type, it can’t be copied. This move-only restriction then applies to the std::vector containing them as well.

std::vector 中使用 std::unique_ptr 是安全的,但有一些限制。unique_ptr 是只允许移动的类型,不能被复制。这个只能移动的限制也适用于包含它们的 std::vector

void create_foo(std::vector<std::unique_ptr<Foo>> &v)
{v.emplace_back(std::make_unique<Foo>(/* ... */));
}

If you want to put an element from this vector into another vector, you must move it to the other vector, as only one unique_ptr at a time can point to the same Foo object.

如果你想把这个vector中的一个元素放到另一个vector中,就必须把它移动到另一个vector中,因为同一时刻只有一个 unique_ptr 指向同一个 Foo 对象。

There are lots of good articles on this general topic, such as Herb Sutter’s in Dr. Dobbs and many others.

关于这个主题有很多不错的文章,比如Herb Sutter在《多布斯博士》(Dr. Dobbs)和其他很多文章。

9、Why are the standard containers so slow? (为什么标准容器这么慢?)

They aren’t, they’re among the fastest on the planet.

它们不是,它们是地球上速度最快的。

Probably “compared to what?” is a more useful question (and answer). When people complain about standard-library container performance, we usually find one of three genuine problems (or one of the many myths and red herrings):

可能是“与什么相比?”是一个更有用的问题(和答案)。当人们抱怨标准库容器的性能时,我们通常会发现以下三个真正的问题之一(或者是许多误解和转移注意力的东西之一):

  • I suffer copy overhead 【承受了复制开销】
  • I suffer slow speed for lookup tables 【查找表的速度很慢】
  • My hand-coded (intrusive) lists are much faster than std::list 【我手工编写的(侵入式的)列表比 std::list 快得多】

Before trying to optimize, consider if you have a genuine performance problem. In most of the cases sent to me, the performance problem is theoretical or imaginary: First measure, then optimize only if needed.

在尝试优化之前,请考虑是否真的存在性能问题。在我收到的大多数案例中,性能问题都是理论上或想象出来的:首先测量,然后只在需要时进行优化。

Let’s look at those problems in turn. Often, a vector<X> is slower than somebody’s specialized My_container<X> because My_container<X> is implemented as a container of pointers to X (brief spoiler: if you want that, you have it too: vector<X*> – more on this in a moment). A vector<X> (no *) holds copies of values, and copies a value when you put it into the container. This is essentially unbeatable for small values, but can be quite unsuitable for huge objects:

让我们依次来看一下这些问题。通常,vector<X> 比某人专用的 My_container<X> 慢,因为 My_container<X> 被实现为指向 X 的指针的容器(简要剧播:如果你想这样,你也有它: ·vector<X*>·——稍后会详细介绍)。vector<X> (没有*)保存值的副本,并且当你将值放入容器时复制它。这对于小的值来说基本上是无敌的,但对于大型对象来说可能非常不合适:

vector<int> vi;
vector<Image> vim;
// ...
int i = 7;
Image im("portrait.jpg");    // initialize image from file
// ...
vi.push_back(i);             // put (a copy of) i into vi
vim.push_back(im);           // put (a copy of) im into vim

Now, if portrait.jpg is a couple of megabytes and Image has value semantics (i.e., copy assignment and copy construction make copies) then vim.push_back(im) will indeed be expensive. But – as the saying goes – if it hurts so much, just don’t do it.

现在,如果 portrait.jpg 有几兆字节,并且图像具有值语义(即,复制赋值和复制构造会产生副本),那么 vim.push_back(im) 确实会很昂贵。但是,就像俗话说的那样,如果真的很疼,那就不要做了。

Move semantics and in-place construction can negate many of these costs if the vector is going to own the object, and you don’t need copies of it elsewhere.

如果vector 要拥有对象,且不需要在其他地方复制对象,那么移动语义和就地构造可以消除这些开销。

vector<Image> vim;
vim.emplace_back("portrait.jpg"); // create image from file in place in the vector

Alternatively, either use a container of handles or a container of pointers. For example, if Image had reference semantics, the code above would incur only the cost of a copy constructor call, which would be trivial compared to most image manipulation operators. If some class, say Image again, does have copy semantics for good reasons, a container of pointers is often a reasonable solution:

或者,使用句柄容器或指针容器。例如,如果 Image 具有引用语义,上面的代码只会产生复制构造函数调用的开销,与大多数图像操作操作符相比,这是微不足道的。如果某些类,比如 Image,有充分的理由具有复制语义,则指针容器通常是一个合理的解决方案:

vector<int> vi;
vector<Image*> vim;
// ...
Image im("portrait.jpg");    // initialize image from file
// ...
vi.push_back(7);             // put (a copy of) 7 into vi
vim.push_back(&im);          // put (a copy of) &im into vim

Naturally, if you use pointers, you have to think about resource management, but containers of pointers can themselves be effective and cheap resource handles (often, you need a container with a destructor for deleting the “owned” objects), or you can simply use a container of smart pointers.

当然,如果你使用指针,你必须考虑资源管理,但是指针容器本身可以是有效且廉价的资源句柄(通常,你需要一个带有析构函数的容器,用于删除“拥有的”对象),或者你可以简单地使用智能指针的容器。

The second frequently occurring genuine performance problem is the use of a map<string,X> for a large number of (string,X) pairs. Maps are fine for relatively small containers (say a few hundred or few thousand elements – access to an element of a map of 10000 elements costs about 9 comparisons), where less-than is cheap, and where no good hash-function can be constructed. If you have lots of strings and a good hash function, use an unordered_map.

第二个经常发生的真正性能问题是用map<string, X>表示大量的 (string, X) 对。map 适用于相对较小的容器(如只有几百或几千个元素——访问包含10000 个元素的 map 中的元素大概需要9次比较),这种情况下, less-than是便宜的,且无法构建良好的散列函数。如果你有很多字符串和一个好的散列函数,请使用 unordered_map

Sometimes, you can speed up things by using (const char*,X) pairs rather than (string,X) pairs, but remember that < doesn’t do lexicographical comparison for C-style strings. Also, if X is large, you may have the copy problem also (solve it in one of the usual ways).

有时,你可以使用 (const char*, X) 对而不是 (string, X) 来加快速度,但请记住 < 不会对C风格的字符串进行字典序比较。另外,如果 X 很大,你可能也会遇到复制问题(用一种常用的方法解决)。

Intrusive lists can be really fast. However, consider whether you need a list at all: A vector is more compact and is therefore smaller and faster in many cases – even when you do inserts and erases. For example, if you logically have a list of a few integer elements, a vector is significantly faster than a list (any list). Also, intrusive lists cannot hold built-in types directly (an int does not have a link member). So, assume that you really need a list and that you can supply a link field for every element type. The standard-library list by default performs an allocation followed by a copy for each operation inserting an element (and a deallocation for each operation removing an element). For std::list with the default allocator, this can be significant. For small elements where the copy overhead is not significant, consider using an optimized allocator. Use a hand-crafted intrusive lists only where a list and the last ounce of performance is needed.

侵入式链表的速度非常快。然而,考虑一下你是否真的需要链表:vector 更紧凑,因此在许多情况下更小、更快——即使在进行插入和删除操作时也是如此。例如,如果逻辑上有一个由几个整数元素组成的链表,那么 vector 明显比链表(任何链表)快得多。此外,侵入式链表不能直接保存内置类型(int没有链接成员)。因此,假设你确实需要一个链表,并且可以为每种元素类型提供一个link字段。在默认情况下,标准库链表为每个插入元素的操作执行分配,并保存副本(并为每个删除元素的操作执行回收)。对于具有默认分配器的 std::list 来说,这可能很重要。对于复制开销不大的小元素,可以考虑使用优化的分配器。仅在需要链表和最后一点性能的地方使用手工制作的侵入式链表。

People sometimes worry about the cost of std::vector growing incrementally. Many C++ programmers used to worry about that and used reserve() to optimize the growth. After measuring their code and repeatedly having trouble finding the performance benefits of reserve() in real programs, they stopped using it except where it is needed to avoid iterator invalidation (a rare case in most code). Again: measure before you optimize.

人们有时会担心 std::vector 的开销会递增。许多C++程序员过去常常担心这个问题,并使用 reserve() 来优化增长。在测量了他们的代码并不断地发现reverse() 没有性能优势后,他们停止使用它,除非需要避免迭代器失效(大多数代码中,这是一种很罕见的情况)。再次强调:优化之前进行测量。

The cost of std::vector growing incrementally in C++11 can be a lot less than it was in C++98/03 when you are using move-aware types, such as std::string or even std::vector<T>, as when the vector is reallocated, the objects are moved into the new storage instead of copied.

当你使用 std::string 甚至 std::vector<T> 等支持移动的类型时,在C++ 11中 std::vector 的增量增长开销比在C++ 98/03中要小得多,因为在重新分配 vector 时,对象会被移动到新的存储空间中,而不是复制。

相关文章:

FAQ:Container Classes篇

1、Why should I use container classes rather than simple arrays?&#xff08;为什么应该使用容器类而不是简单的数组&#xff1f;&#xff09; In terms of time and space, a contiguous array of any kind is just about the optimal construct for accessing a sequen…...

每日一题(LeetCode)----栈和队列--滑动窗口最大值

每日一题(LeetCode)----栈和队列–滑动窗口最大值 1.题目&#xff08;239. 滑动窗口最大值&#xff09; 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 …...

13.bash shell中的if-then语句

文章目录 shell中的流控制if语句if语句if-then语句if-then-else 语句 test命令数值比较字符串比较文件比较case语句 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; shell中的流控制if语句 简单的脚本可以只包含顺序执行的命令&#xff0…...

深入了解 Python 的 import 语句

在 Python 中&#xff0c;import 语句是一个关键的功能&#xff0c;用于在程序中引入模块和包。本文将深入讨论 import 语句的各种用法、注意事项以及一些高级技巧&#xff0c;以帮助你更好地理解和使用这一功能。 概念介绍 package 通常对应一个文件夹&#xff0c;下面可以有…...

接口测试 — 11.logging日志模块处理流程

1、概括理解 了解了四大组件的基本定义之后&#xff0c;我们通过图示的方式来理解下信息的传递过程&#xff1a; 也就是获取的日志信息&#xff0c;进入到Logger日志器中&#xff0c;传递给处理器确定要输出到哪里&#xff0c;然后进行过滤器筛选&#xff0c;通过后再按照定义…...

Hago 的 Spark on ACK 实践

作者&#xff1a;华相 Hago 于 2018 年 4 月上线&#xff0c;是欢聚集团旗下的一款多人互动社交明星产品。Hago 融合优质的匹配能力和多样化的垂类场景&#xff0c;提供互动游戏、多人语音、视频直播、 3D 虚拟形象互动等多种社交玩法&#xff0c;致力于为用户打造高效、多样、…...

mac传输文件到windows

前言 由于mac系统与windows系统文件格式不同&#xff0c;通过U盘进行文件拷贝时&#xff0c;导致无法拷贝。官方解决方案如下&#xff0c;但是描述的比较模糊。看我的操作步骤即可。 https://support.apple.com/zh-cn/guide/mac-help/mchlp1657/12.0/mac/12.6 前提条件 mac与…...

trtc-electron-sdk的demo中添加更新功能以及出现的报错问题

1、官网demo下载地址 点击下载 按照官网demo说明文档进行安装和运行 2、添加electron-updater npm install electron-updater根据项目需求安装对应的版本&#xff0c;建议使用5.2.1 3、创建一个handleUpdater.js文件&#xff0c;和package.json同级 // const { ipcMain } …...

什么是流量攻击? 流量攻击怎么处理?

由于DDoS攻击往往采取合法的数据请求技术&#xff0c;再加上傀儡机器&#xff0c;造成DDoS攻击成为最难防御的网络攻击之一。据美国最新的安全损失调查报告&#xff0c;DDoS攻击所造成的经济损失已经跃居第一。 传统的网络设备和周边安全技术&#xff0c;例如防火墙和IDSs(Intr…...

【大数据】NiFi 的基本使用

NiFi 的基本使用 1.NiFi 的安装与使用1.1 NiFi 的安装1.2 各目录及主要文件 2.NiFi 的页面使用2.1 主页面介绍2.2 面板介绍 3.NiFi 的工作方式3.1 基本方式3.2 选择处理器3.3 组件状态3.4 组件的配置3.4.1 SETTINGS&#xff08;通用配置&#xff09;3.4.2 SCHEDULING&#xff0…...

5 分钟内搭建一个免费问答机器人:Milvus + LangChain

搭建一个好用、便宜又准确的问答机器人需要多长时间&#xff1f; 答案是 5 分钟。只需借助开源的 RAG 技术栈、LangChain 以及好用的向量数据库 Milvus。必须要强调的是&#xff0c;该问答机器人的成本很低&#xff0c;因为我们在召回、评估和开发迭代的过程中不需要调用大语言…...

WPF Border

在 WPF 中&#xff0c;Border 是一种常用的控件&#xff0c;用于给其他控件提供边框和背景效果。 要使用 Border 控件&#xff0c;您可以在 XAML 代码中添加以下代码&#xff1a; <Border BorderBrush"Black" BorderThickness"2" Background"Lig…...

基于博弈树的开源五子棋AI教程[4 静态棋盘评估]

引子 静态棋盘的评估是棋力的一个很重要的体现&#xff0c;一个优秀的基于博弈树搜索的AI往往有上千行工作量&#xff0c;本文没有做深入讨论&#xff0c;仅仅写了个引子用来抛砖引玉。 评估一般从两个角度入手&#xff0c;一个是子力&#xff0c;另一个是局势。 1 评估维度 …...

STL--排序与检索

题目 现有N个大理石&#xff0c;每个大理石上写了一个非负整数。首先把各数从小到大排序&#xff0c;然后回答Q个问题。每个问题是否有一个大理石写着某个整数x,如果是&#xff0c;还要回答哪个大理石写着x。排序后的大理石从左到右编写为1-N。&#xff08;样例中&#xff0c;…...

大数据处理与分析-Spark

导论 (基于Hadoop的MapReduce的优缺点&#xff09; MapReduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于Hadoop的数据分析应用”的核心框架 MapReduce是一种用于处理大规模数据集的编程模型和计算框架。它将数据处理过程分为两个主要阶段&#xff1a;Map阶…...

虚拟机的下载、安装(模拟出服务器)

下载 vmware workstation&#xff08;收费的虚拟机&#xff09; 下载vbox 网址&#xff1a;Oracle VM VirtualBox&#xff08;免费的虚拟机&#xff09; 以下选择一个下载即可&#xff0c;建议下载vbox&#xff0c;因为是免费的。安装的时候默认下一步即可&#xff08;路径最好…...

K8S Pod Terminating/Unknown故障排查

一、pod异常出现现象 优雅终止周期(Graceful termination period): 当pod被删除时&#xff0c;会进入"Terminating"状态&#xff0c;等待容器优雅关闭。如果容器关闭所需时间超过默认期限(默认30秒)&#xff0c;则pod将保持在"Terminating"状态。 Finalize…...

labelme标注的json文件数据转成coco数据集格式(可处理目标框和实例分割)

这里主要是搬运一下能找到的 labelme标注的json文件数据转成coco数据集格式&#xff08;可处理目标框和实例分割&#xff09;的代码&#xff0c;以供需要时参考和提供相关帮助。 1、官方labelme实现 如下是labelme官方网址&#xff0c;提供了源代码&#xff0c;以及相关使用方…...

MySQL报错:1366 - Incorrect integer value: ‘xx‘ for column ‘xx‘ at row 1的解决方法

我在插入表数据时遇到了1366报错&#xff0c;报错内容&#xff1a;1366 - Incorrect integer value: Cindy for column name at row 1&#xff0c;下面我演示解决方法。 根据上图&#xff0c;原因是Cindy’对应的name字段数据类型不正确。我们在左侧找到该字段所在的grade_6表&…...

MySQL中MVCC的流程

参考文章一 参考文章二 当谈到数据库的并发控制时&#xff0c;多版本并发控制&#xff08;MVCC&#xff09;是一个重要的概念。MVCC 是一种用于实现数据库事务隔离性的技术&#xff0c;常见于像 PostgreSQL 和 Oracle 这样的数据库系统中。 MVCC 的核心思想是为每个数据行维护…...

朴素贝叶斯法_naive_Bayes

朴素贝叶斯法&#xff08;naive Bayes&#xff09;是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集&#xff0c;首先基于特征条件独立假设学习输入输出的联合概率分布&#xff1b;然后基于此模型&#xff0c;对给定的输入 x x x&#xff0c;利用贝叶斯定理…...

Windows下安装MongoDB实践总结

本文记录Windows环境下的MongoDB安装与使用总结。 【1】官网下载 官网下载地址&#xff1a;Download MongoDB Community Server | MongoDB 这里可以选择下载zip或者msi&#xff0c;zip是解压后自己配置&#xff0c;msi是傻瓜式一键安装。这里我们分别对比进行实践。 【2】ZI…...

华为云Stack 8.X 流量模型分析(二)

二、流量模型分析相关知识 1.vNIC ​ 虚拟网络接口卡(vNIC)是基于主机物理 NIC 的虚拟网络接口。每个主机可以有多个 NIC&#xff0c;每个 NIC 可以是多个 vNIC 的基础。 ​ 将 vNIC 附加到虚拟机时&#xff0c;Red Hat Virtualization Manager 会在虚拟机之间创建多个关联的…...

rk3588 之启动

目录 uboot版本配置修改编译 linux版本配置修改编译 启动sd卡启动制作spi 烧录 参考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用这两个配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…...

ARM GIC (五)gicv3架构-LPI

在gicv3中,引入了一种新的中断类型。message based interrupts,消息中断。 一、消息中断 外设,不在通过专用中断线,向gic发送中断,而是写gic的寄存器,来发送中断。 这样的一个好处是,可以减少中断线的个数。 为了支持消息中断,gicv3,增加了LPI,来支持消息中断。并且…...

sql-labs服务器结构

双层服务器结构 一个是tomcat的jsp服务器&#xff0c;一个是apache的php服务器&#xff0c;提供服务的是php服务器&#xff0c;只是tomcat向php服务器请求数据&#xff0c;php服务器返回数据给tomcat。 此处的29-32关都是这个结构&#xff0c;不是用docker拉取的镜像要搭建一下…...

【小沐学写作】Docsify制作在线电子书、技术文档(Docsify + Markdown + node)

文章目录 1、简介2、安装2.1 node2.2 docsify-cli 3、配置3.1 初始化3.2 预览效果3.3 加载对话框3.4 更多页面3.5 侧 栏3.6 自定义导航栏 结语 1、简介 https://docsify.js.org/#/?iddocsify 一个神奇的文档网站生成器。 简单轻巧没有静态构建的 html 文件多个主题 Docsify…...

电脑完全重装教程——原版系统镜像安装

注意事项 本教程会清除所有个人文件 请谨慎操作 请谨慎操作 请谨慎操作 前言 本教程是以系统安装U盘为介质进行系统重装操作&#xff0c;照着流程操作会清除整个硬盘里的文件&#xff0c;请考虑清楚哦&#xff5e; 有些小伙伴可能随便在百度上找个WinPE作为启动盘就直接…...

【智慧办公】如何让智能会议室的电子标签实现远程、批量更新信息?东胜物联网硬件网关让解决方案更具竞争力

近年来&#xff0c;为了减少办公耗能、节能环保、降本增效&#xff0c;越来越多的企业开始从传统的办公模式转向智慧办公。 以智能会议室为例&#xff0c;会议是企业业务中不可或缺的一部分&#xff0c;但在传统办公模式下&#xff0c;一来会议前行政人员需要提前准备会议材料…...

面向对象设计与分析40讲(16)静态工厂方法模式

前面我们介绍了简单工厂模式&#xff0c;在创建对象前&#xff0c;我们需要先创建工厂&#xff0c;然后再通过工厂去创建产品。 如果将工厂的创建方法static化&#xff0c;那么无需创建工厂即可通过静态方法直接调用的方式创建产品&#xff1a; // 工厂类&#xff0c;定义了静…...

【贪心】买卖股票的最佳时机含手续费

/** 贪心&#xff1a;每次选取更低的价格买入&#xff0c;遇到高于买入的价格就出售(此时不一定是最大收益)。* 使用buy表示买入股票的价格和手续费的和。遍历数组&#xff0c;如果后面的股票价格加上手续费* 小于buy&#xff0c;说明有更低的买入价格更新buy。如…...

Altium Designer入门到就业【目录】

&#x1f3e1;《AD目录》 欢迎大家来到《Altium Designer入门到就业》该专栏包括【电路设计篇】【PCB设计篇】【电路仿真篇】【PCB仿真篇】四个部分&#xff0c;以供大家参考。大家直接点击大纲中蓝色标题即可轻松传送。 【电路设计篇】 Altium Designer&#xff08;AD24&#…...

cmake 查看编译命令,以及在vscode中如何使用cmke

通过设置如下配置选项&#xff0c;可以生成compile_commands.json 文件&#xff0c;记录使用的编译命令 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)获得现有模块列表 cmake --help-module-list查看命令文档 cmake --help-command find_file查看模块的详细信息 cmake --help-mo…...

玩转 Scrapy 框架 (一):Scrapy 框架介绍及使用入门

目录 一、Scrapy 框架介绍二、Scrapy 入门 一、Scrapy 框架介绍 简介&#xff1a; Scrapy 是一个基于 Python 开发的爬虫框架&#xff0c;可以说它是当前 Python 爬虫生态中最流行的爬虫框架&#xff0c;该框架提供了非常多爬虫的相关组件&#xff0c;架构清晰&#xff0c;可扩…...

node.js mongoose index(索引)

目录 简介 索引类型 单索引 复合索引 文本索引 简介 在 Mongoose 中&#xff0c;索引&#xff08;Index&#xff09;是一种用于提高查询性能的数据结构&#xff0c;它可以加速对数据库中文档的检索操作 索引类型 单索引、复合索引、文本索引、多键索引、哈希索引、地理…...

谷歌推大语言模型VideoPoet:文本图片皆可生成视频和音频

Google Research最近发布了一款名为VideoPoet的大型语言模型&#xff08;LLM&#xff09;&#xff0c;旨在解决当前视频生成领域的挑战。该领域近年来涌现出许多视频生成模型&#xff0c;但在生成连贯的大运动时仍存在瓶颈。现有领先模型要么生成较小的运动&#xff0c;要么在生…...

ES-mapping

类似数据库中的表结构定义&#xff0c;主要作用如下 定义Index下的字段名( Field Name) 定义字段的类型&#xff0c;比如数值型、字符串型、布尔型等定义倒排索引相关的配置&#xff0c;比如是否索引、记录 position 等 index_options 用于控制倒排索记录的内容&#xff0c;有如…...

Centos 7.9安装Oracle19c步骤亲测可用有视频

视频介绍了在虚拟机安装centos 7.9并安装数据库软件的全过程 视频链接&#xff1a;https://www.zhihu.com/zvideo/1721267375351996416 下面的文字描述是安装数据库的部分介绍 一.安装环境准备 链接&#xff1a;https://pan.baidu.com/s/1Ogn47UZQ2w7iiHAiVdWDSQ 提取码&am…...

.NET中的Swagger使用

目录 前言 一、Swagger是什么&#xff1f; 二、如何Swagger文档说明的信息 1.在AddSwaggerGen方法中写入文档信息 2.运行效果 二、文档UI界面标题、路由设置 1.在中间件UseSwaggerUI方法中配置 三、文档UI界面添加接口注释 1.在 .csproj中配置 2.在AddSwaggerGen方法中配置Incl…...

结构屈曲分析

结构屈曲分析主要用于判定结构受载后是否有失稳风险&#xff0c;作为工程应用&#xff0c;一般分为线性屈曲分析和非线性屈曲分析。 线性屈曲分析需要具备较多的前提条件&#xff0c;如载荷无偏心、材料无缺陷等&#xff0c;在实际工程应用中结构制作过程和加载方式很难达到线性…...

Flink 客户端操作命令及可视化工具

Flink提供了丰富的客户端操作来提交任务和与任务进行交互。下面主要从Flink命令行、Scala Shell、SQL Client、Restful API和 Web五个方面进行整理。 在Flink安装目录的bin目录下可以看到flink&#xff0c;start-scala-shell.sh和sql-client.sh等文件&#xff0c;这些都是客户…...

csrf自动化检测调研

https://github.com/pillarjs/understanding-csrf/blob/master/README_zh.md CSRF 攻击者在钓鱼站点&#xff0c;可以通过创建一个AJAX按钮或者表单来针对你的网站创建一个请求&#xff1a; <form action"https://my.site.com/me/something-destructive" metho…...

记录一个Python鼠标自动模块用法和selenium加载网页插件的设置

写爬虫&#xff0c;或者网页自动化&#xff0c;让程序自动完成一些重复性的枯燥的网页操作&#xff0c;是最常见的需求。能够解放双手&#xff0c;空出时间看看手机&#xff0c;或者学习别的东西&#xff0c;甚至还能帮朋友亲戚减轻工作量。 然而&#xff0c;网页自动化代码编写…...

【数据库系统概论】第3章-关系数据库标准语言SQL(1)

文章目录 3.1 SQL概述3.2 学生-课程数据库3.3 数据定义3.3.1 数据库定义3.3.2 模式的定义3.3.3 基本表的定义3.3.4 索引的建立与删除3.3.5 数据字典 3.1 SQL概述 动词 分类 三级模式 3.2 学生-课程数据库 3.3 数据定义 3.3.1 数据库定义 创建数据库 tips&#xff1a;[ ]表…...

【Python】基于flaskMVT架构与session实现博客前台登录登出功能

目录 一、MVT说明 1.Model层 2.View层 3.Template层 二、功能说明 三、代码框架展示 四、具体代码实现 models.py 登录界面前端代码 博客界面前端代码&#xff08;profile.html&#xff09; main.py 一、MVT说明 MVT架构是Model-View-Template的缩写&#xff0c;是…...

为什么有的开关电源需要加自举电容?

一、什么是自举电路&#xff1f; 1.1 自举的概念 首先&#xff0c;自举电路也叫升压电路&#xff0c;是利用自举升压二极管&#xff0c;自举升压电容等电子元件&#xff0c;使电容放电电压和电源电压叠加&#xff0c;从而使电压升高。有的电路升高的电压能达到数倍电源电压。…...

【MCAL】TC397+EB-treso之MCU配置实战 - 芯片时钟

本篇文章介绍了在TC397平台使用EB-treso对MCU驱动模块进行配置的实战过程&#xff0c;主要介绍了后续基本每个外设模块都要涉及的芯片时钟部分&#xff0c;帮助读者了解TC397芯片的时钟树结构&#xff0c;在后续计算配置不同外设模块诸如通信速率&#xff0c;定时器周期等&…...

高级人工智能之群体智能:蚁群算法

群体智能 鸟群&#xff1a; 鱼群&#xff1a; 1.基本介绍 蚁群算法&#xff08;Ant Colony Optimization, ACO&#xff09;是一种模拟自然界蚂蚁觅食行为的优化算法。它通常用于解决路径优化问题&#xff0c;如旅行商问题&#xff08;TSP&#xff09;。 蚁群算法的基本步骤…...

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

【SpringBoot应用篇】【AOP注解】SpringBootSpEL表达式基于注解实现权限控制 Spring SpEL基本表达式类相关表达式表达式模板 SpEL表达式实现权限控制PreAuthAuthFunPreAuthAspectUserControllerSpelParserUtils Spring SpEL Spring 表达式语言 SpEL 是一种非常强大的表达式语言…...

Java研学-HTTP 协议

一 概述 1 概念和作用 概念&#xff1a;HTTP 是 HyperText Transfer Protocol (超文本传输协议)的简写&#xff0c;它是 TCP/IP 协议之上的一个应用层协议。简单理解就是 HTTP 协议底层是对 TCP/IP 协议的封装。   作用&#xff1a;用于规定浏览器和服务器之间数据传输的格式…...