与 C++ 中的对象数组混淆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14345031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:14:42  来源:igfitidea点击:

Confused with object arrays in C++

c++arrays

提问by Eric Smith

So I first learned Java and now I'm trying to switch over to C++. I'm having a little difficulty getting arrays to work correctly.

所以我首先学习了 Java,现在我正在尝试切换到 C++。我在让数组正常工作时遇到了一些困难。

Right now I am simply trying to create an array of object "Player" and populate it with one. But I get an error.

现在我只是想创建一个对象“播放器”的数组并用一个填充它。但我得到一个错误。

Player* players = new Player[1];
players[0] = new Player(playerWidth, playerHeight, 20, 1);

The error says: the operand "=" matches these operands. Operand types are: Player = Player *

错误说:操作数“=”匹配这些操作数。操作数类型为: Player = Player *

I can't understand why this doesn't work?

我不明白为什么这不起作用?

回答by bames53

What the error is saying is that you are trying to assign a value of the wrong type to the variable. When the error says Player = Player *that means that the variable on the left hand side is a Playerand the value on the right hand side is a Player *.

错误的意思是您试图将错误类型的值分配给变量。当错误表示Player = Player *这意味着左侧的变量是 aPlayer而右侧的值是 a Player *

players[0] = new Player(playerWidth, playerHeight, 20, 1);

The problem is similar to if you were to do:

问题类似于您要执行的操作:

int x;
x = "Hello, World!";

The left and right hand types don't match, and there's no natural conversion, so you get an error.

左右手类型不匹配,没有自然转换,所以你会得到一个错误。



The first problem is that you're coming from a Java background, and Java uses pointers a lot but hides them from you. C++ doesn't hide them at all. The consequence is that C++ has different syntax for explicitly dealing with pointers. Java got rid of all that and mostly used the regular, non-pointer syntax from C++ for dealing with pointers.

第一个问题是您来自 Java 背景,Java 经常使用指针,但对您隐藏它们。C++ 根本没有隐藏它们。结果是 C++ 有不同的语法来显式处理指针。Java 摆脱了所有这些,主要使用 C++ 中的常规非指针语法来处理指针。

Java:                                  C++:

Player player = new Player();          Player *player = new Player();

Player player2;                        Player *player2 = nullptr;

** no equivalent in java **            Player player3;

player.foo();                          player->foo();

** no equivalent in java **            player3.foo();

** no equivalent in java **            *player;

** no equivalent in java **            &player2;

It's very important to understand the difference between working with pointers and working directly with an object:

了解使用指针和直接使用对象之间的区别非常重要:

Java:                                  C++:

Player a = new Player();               Player *a = new Player();
Player b = a;                          Player *b = a;
b.foo();                               b->foo();

In this code there's only a single object, and you can access it through either aor band it doesn't make a difference, aand bare both pointers to the same object.

在这段代码中只有一个对象,你可以通过aor访问它b,它没有区别,a并且b都是指向同一个对象的指针。

C++:

Player c = Player();
Player d = c;
d.foo();

In this code there are two objects. They are distinct, and doing something to ddoes not affect c.

在这段代码中有两个对象。它们是不同的,做一些事情d不会影响c.

If in Java you learned about the distinction between 'primitive' types like intand Object types like Stringthen one way to think about it is that in C++ all objects are primitive. If we look back at your code and use this 'C++ objects are like Java primitives' rule you can maybe see better what's wrong:

如果在 Java 中您了解了“原始”类型(如)int和对象类型(如)之间的区别,String那么一种思考方式是在 C++ 中所有对象都是原始类型。如果我们回顾一下你的代码并使用这个“C++ 对象就像 Java 原语”规则,你可能会更好地看到什么是错的:

Java:
int[] players = new int[1];
players[0] = new int(playerWidth); // huh???

That should make it clear that the right hand side of the assignment should simply be a Player value rather than a dynamic allocation of a new player object. For an int in java this looks like players[0] = 100;. Since Object types in Java are different Java doesn't have a way to write Object valuesthe way you can write intvalues. But C++ does; players[0] = Player(playerWidth, playerHeight, 20, 1);

这应该清楚地表明,赋值的右侧应该只是一个 Player 值,而不是一个新玩家对象的动态分配。对于 java 中的 int,这看起来像players[0] = 100;. 由于 Java 中的对象类型不同,Java 无法像编写那样编写对象int值。但是 C++ 可以;players[0] = Player(playerWidth, playerHeight, 20, 1);



The second problem is that arrays in C are weird and C++ inherited that.

第二个问题是 C 中的数组很奇怪,而 C++ 继承了它。

Pointers in C and C++ allow 'pointer arithmetic. If you have a pointer to an object you can add to or subtract from it and get a pointer to a different object. Java has nothing similar to this.

C 和 C++ 中的指针允许“指针算术”。如果你有一个指向一个对象的指针,你可以对它进行加法或减法,并得到一个指向不同对象的指针。Java 没有类似的东西。

int x[2]; // create an array of two ints, the ints are 'adjacent' to one another
// if you take the address for the first one and 'increment' it
// then you'll have a pointer to the second one.

int *i = &x[0]; // i is a pointer to the first element
int *j = &x[1]; // j is a pointer to the second element

// i + 1 equals j
// i equals j - 1

Additionally the array index operator []works on pointers. x[5]is equivalent to *(x+5). This means that pointers can be used as arrays, and that is idiomatic and expected in C and C++. In fact it's even baked into C++.

此外,数组索引运算符[]适用于指针。x[5]相当于*(x+5)。这意味着指针可以用作数组,这在 C 和 C++ 中是惯用的和预期的。事实上,它甚至已经融入到 C++ 中。

In C++ when you use newto dynamically allocate an object, e.g. new Player, you normally get a pointer to the type you specified. In this example you get Player *. But when you dynamically allocate an array, e.g. new Player[5], it's different. Instead of getting back a pointer to an array of five Players, you actually get back a pointer to the first element. This is just like any other Player *:

在 C++ 中,当您使用new动态分配对象时,例如new Player,您通常会获得一个指向您指定的类型的指针。在这个例子中,你得到Player *. 但是当你动态分配一个数组时,例如new Player[5],它是不同的。Players你实际上得到了一个指向第一个元素的指针,而不是返回一个指向 5 数组的指针。这就像任何其他Player *

Player *p   = new Player;    // not an array
Player *arr = new Player[5]; // an array

The only thing that makes this pointer different is that when you do pointer arithmetic on it you get pointers to valid Playerobjects:

唯一使这个指针不同的是,当你对其进行指针运算时,你会得到指向有效Player对象的指针:

Player *x = p + 1;   // not pointing at a valid Player
Player *y = arr + 3; // pointing at the fourth array element


newand deleteare hard to use correctly if you use them without protection. To demonstrate this:

newdelete如果您在没有保护的情况下使用它们,则很难正确使用。为了证明这一点:

int *x = new int;
foo();
delete x;

This code is error prone and probably wrong. Specifically, if foo()throws an exception then xis leaked.

这段代码很容易出错,而且可能是错误的。具体来说,如果foo()抛出异常,则x泄漏。

In C++ whenever you acquire a responsibility, such as when you call newyou acquire the responsibility to call deleteat a later time, you should remember

在 C++ 中,每当您获得责任时,例如当您调用时,您将new获得delete稍后调用的责任,您应该记住

R.A.I.I.
Responsibility* Acquisition Is Initialization

RAII
Responsibility* 收购即初始化

* More frequently people say 'resource acquisition is initialization', but resources are only one kind of responsibility. I was persuaded to use the latter term by Jon Kalb in one of his Exception Safe C++talks.

* 更多人说“资源获取就是初始化”,但资源只是一种责任。我被 Jon Kalb 在他的一次Exception Safe C++演讲中说服使用后一个术语。

R.A.I.I. means that whenever you acquire a responsibility, it should look like you're initializing an object; specifically you're initializing a special object who's purpose is to manage that responsibility for you. One example of such an type is std::unique_ptr<int>which will manage pointers to ints allocated with new:

RAII 意味着每当您获得责任时,它看起来就像您正在初始化一个对象;具体来说,您正在初始化一个特殊对象,其目的是为您管理该责任。这种类型的一个例子是std::unique_ptr<int>它将管理指向ints 分配的指针new

C++:

std::unique_ptr<int> x(new int);
foo();
// no 'delete x;'

To manage your Playerarray you'd use std::unqiue_ptrlike this:

要管理您的Player阵列,您可以std::unqiue_ptr像这样使用:

std::unique_ptr<Player[]> players(new Player[1]);
players[0] = Player(playerWidth, playerHeight, 20, 1);

Now the unique_ptrwill handle that allocation for you and you don't need to call deleteyourself. (N.B. when you allocate an array you should give unique_ptran array type; std::unique_ptr<Player[]>, and when you allocate anything else you use a non-array type, std::unique_ptr<Player>.)

现在unique_ptr将为您处理分配,您无需给delete自己打电话。(注意,当你分配一个数组时,你应该给出unique_ptr一个数组类型;std::unique_ptr<Player[]>当你分配其他任何东西时,你使用非数组类型,std::unique_ptr<Player>。)

Of course C++ has an even more specialized R.A.I.I. type for managing arrays, std::vector, and you should prefer that to using std::unique_ptr:

当然,C++ 有一个更专门的 RAII 类型来管理数组,std::vector,你应该更喜欢使用它std::unique_ptr

std::vector<Player> players(1);
players[0] = Player(playerWidth, playerHeight, 20, 1);

Or in C++11:

或者在 C++11 中:

std::vector<Player> players { Player(playerWidth, playerHeight, 20, 1) };

回答by Lily Ballard

Your types don't match. And it's no wonder, you're trying to store a Player*into an already-allocated Player!

你的类型不匹配。难怪,您正试图将 a 存储Player*到已分配的Player!

Player* players = new Player[1];

This creates an array of length 1, containing an instantiated Player, and stores the whole thing into a Player*. The type of players[0]is going to be Player.

这将创建一个长度为 1 的数组,其中包含一个实例化的Player,并将整个内容存储到一个 中Player*。的类型players[0]将是Player

players[0] = new Player(...)

This attempts to create a new Player*and store it in the array. But the array contains Playerobjects. You should just say

这会尝试创建一个新的Player*并将其存储在数组中。但是数组包含Player对象。你应该说

players[0] = Player(...)


Alternatively, and I'm going to guess this is more appropriate for you, you should stop using newentirely, and use a std::vector.

或者,我猜这更适合您,您应该new完全停止使用,并使用std::vector.

std::vector<Player> players;
players.push_back(Player(playerWidth, playerHeight, 20, 1));
// or players.emplace_back(playerWidth, playerHeight, 20, 1);

Not only is this much easier to use, but you also don't have to remember to deleteit later. When the std::vectorgoes out of scope, it will automatically destruct. Also, unlike your array, std::vectorcan contain any number of objects, so you can add new players or delete existing players at will.

这不仅更容易使用,而且您以后也不必记住delete它。当std::vector超出范围时,它将自动销毁。此外,与您的数组不同,它std::vector可以包含任意数量的对象,因此您可以随意添加新玩家或删除现有玩家。

There are other data structures as well that may possibly be more suited for you, depending on your exact use, but std::vectoris a good starting point.

还有其他数据结构可能更适合您,具体取决于您的具体用途,但这std::vector是一个很好的起点。

回答by DarkWanderer

The reason is, type of your variable

原因是,您的变量类型

players[0]

is Player (object). However, operator "new" (new Player) returns a pointer (Player*)

是玩家(对象)。但是,运算符“new”(新播放器)返回一个指针(播放器*)

If you want to have only one object, correct way to do it will be:

如果你只想拥有一个对象,正确的做法是:

Player* player = new Player(playerWidth, playerHeight, 20, 1);

And don't forget in C++ you need to clean the mess after yourself - somewhere in the end call

并且不要忘记在 C++ 中你需要自己清理烂摊子 - 最后调用

delete player;

for every object you've created. C++ does not have Garbage Collector - meaning all manually created (by "new") objects stay until you manually delete them.

对于您创建的每个对象。C++ 没有垃圾收集器——这意味着所有手动创建的(由“新”创建的)对象会一直存在,直到您手动删除它们。

回答by Porkbutts

In Java, when you use the keyword "new" you actually get back a pointer to an object. This is the only way to instantiate an object type in Java. So when you say you have an "Array of objects" in Java, it's more correct to say that you have an array of pointers to objects.

在 Java 中,当您使用关键字“new”时,您实际上会得到一个指向对象的指针。这是在 Java 中实例化对象类型的唯一方法。因此,当您说在 Java 中有一个“对象数组”时,更正确的说法是您有一个指向对象的指针数组。

C++ does not hide the fact that objects are pointers. You can have a variable referencing an object, or you can have a variable referencing a pointer to an object.

C++ 并没有隐藏对象是指针的事实。您可以让变量引用对象,也可以让变量引用指向对象的指针。

In your example, you need to explicitly declare it an as an array of pointers to objects.

在您的示例中,您需要将其显式声明为指向对象的指针数组。

Players **players = new (Player*)[1];                         // Create an array of player pointers
players[0] = new Player(playerWidth, playerHeight, 20, 1);    // Create a single player

And while C++ allows you to explicitly create objects using the keyword new, you must be sure to cleanup your objects once you are done, otherwise they will never be deallocated (known as a memory leak).

虽然 C++ 允许您使用关键字new显式创建对象,但您必须确保在完成后清理您的对象,否则它们将永远不会被释放(称为内存泄漏)。

This is one of the major differences between C++ and Java; Java's objects are garbage collected and the programmer doesn't have to worry about managing the lifetime of an object.

这是 C++ 和 Java 之间的主要区别之一;Java 的对象是垃圾收集的,程序员不必担心管理对象的生命周期。

Once you are done, you will need to cleanup both the individual player that you allocated, as well as the array. Good rule of thumb is that every call to newshould correspond to a call to delete.

完成后,您需要清理分配的单个玩家以及数组。好的经验法则是每次调用new都应该对应一个调用delete

delete players[0];  // delete the player pointed to by players[0]
delete[] players;   // syntax for deleting arrays

However, something interesting to note is that unlike Java, where objects are allocated on the heap, you can create objects on the stack in C++ as if they were primitive types (like int, float, char). This allows you to have objects that are locally scoped, as well as contiguously aligned in memory. There is no way to do this in Java.

然而,值得注意的是,与 Java 在堆上分配对象不同,您可以在 C++ 中在堆栈上创建对象,就好像它们是原始类型(如 int、float、char)一样。这允许您拥有局部范围的对象,以及在内存中连续对齐的对象。在 Java 中没有办法做到这一点。

If you allocate an array of objects this way, then the default constructor is called for each object in the array.

如果以这种方式分配对象数组,则会为数组中的每个对象调用默认构造函数。

Player p;                           // This calls the default constructor and returns a Player object

Players *players = new Player[5];   // Create an array of player objects
players[0].playerWidth = 8;         // valid because the object has already been constructed

delete[] players; // don't forget to cleanup the array.
                  // no need to cleanup individual player objects, as they are locally scoped.

EDIT:As some others have mentioned, using a std::vector instead of an array is probably easier in your case (no need to worry about memory allocation) and is on the same order of performance as an array; however I think it is extremely important to become comfortable with the notion of pointers in C++ as they help you understand how memory is organized.

编辑:正如其他一些人所提到的,在您的情况下使用 std::vector 而不是数组可能更容易(无需担心内存分配)并且与数组的性能顺序相同;但是我认为熟悉 C++ 中的指针概念非常重要,因为它们可以帮助您理解内存的组织方式。

Here is the syntax for creating a vector of Player pointers.

这是创建 Player 指针向量的语法。

std::vector<Player*> players(1); // Creates a vector of pointer to player with length 1
players[0] = new Player(playerWidth, playerHeight, 20, 1); // Create a new player object
delete players[0];                                         // delete the player

And the syntax for creating a vector of actual Player object instances (this one is the most preferred solution):

以及创建实际 Player 对象实例向量的语法(这是最首选的解决方案):

std::vector<Player> players(5); // Creates a vector of five player objects
players[0].playerWidth = 8; //already constructed, so we can edit immediately
//no cleanup required for the vector _or_ the players.

回答by Ulrich Eckhardt

In Java, you do Foo f = new Foo();, giving you a dynamically allocated object who's lifetime is managed by the garbage collector.

在 Java 中,您可以这样做Foo f = new Foo();,为您提供一个动态分配的对象,该对象的生命周期由垃圾收集器管理。

Now, in C++, Foo* f = new Foo;looks similar and also gives you a dynamically allocated object (which you can access via the pointer f), but C++ doesn't have a built-in garbage collector. In most cases, the functional C++ equivalent is Foo f;, which gives you a local object that is destroyed when you leave the current function (via return or throw).

现在,在 C++ 中,Foo* f = new Foo;看起来类似并且还为您提供了一个动态分配的对象(您可以通过指针访问f),但 C++ 没有内置垃圾收集器。在大多数情况下,函数式 C++ 等价物是Foo f;,它为您提供一个本地对象,当您离开当前函数(通过 return 或 throw)时该对象将被销毁。

If you need dynamic allocation, use "smart pointers", which are actually classes that behave like pointers. In C++ 98, there is only std::auto_ptrand people often use boost::shared_ptrto complement it. In the newer C++ 11, there are std::unique_ptrand std::shared_ptrthat achieve the same.

如果您需要动态分配,请使用“智能指针”,它们实际上是行为类似于指针的类。在 C++ 98 中,只有std::auto_ptr并且人们经常使用boost::shared_ptr它来补充它。在较新的 C++ 11 中,有std::unique_ptrstd::shared_ptr实现相同的。

I hope this gives you some pointers in directions where you need to read a bit, but overall Juanchopanza gave a good advise: Don't use newunless you really need to. Good luck!

我希望这会给你一些需要阅读的方向的指示,但总的来说 Juanchopanza 给出了一个很好的建议:new除非你真的需要,否则不要使用。祝你好运!

回答by Jelly

Here you have allocated some memory to store an array of one Player (not really useful but it's a first step).

在这里,您分配了一些内存来存储一个 Player 的数组(不是很有用,但这是第一步)。

Your variable "players" is now storing the address of the first (and only) slot in this array. Then by accessing the first Player with players[0], you are able to directly write/read in its memory and no more allocation is needed.

您的变量“players”现在存储该数组中第一个(也是唯一一个)插槽的地址。然后通过使用 player[0] 访问第一个 Player,您可以直接在其内存中写入/读取,无需更多分配。