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

Eri*_*ith 15 c++ arrays

所以我第一次学习Java,现在我正在尝试切换到C++.我让阵列正常工作有点困难.

现在我只是想创建一个对象"Player"的数组并用一个填充它.但是我收到了一个错误.

Player* players = new Player[1];
players[0] = new Player(playerWidth, playerHeight, 20, 1);
Run Code Online (Sandbox Code Playgroud)

错误说:操作数"="匹配这些操作数.操作数类型有:Player = Player*

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

bam*_*s53 46

错误的含义是您正在尝试为变量分配错误类型的值.当错误表示Player = Player *这意味着左侧的变量是a Player而右侧的值是a Player *.

players[0] = new Player(playerWidth, playerHeight, 20, 1);
Run Code Online (Sandbox Code Playgroud)

如果您这样做,问题类似于:

int x;
x = "Hello, World!";
Run Code Online (Sandbox Code Playgroud)

左手和右手类型不匹配,并且没有自然转换,因此您会收到错误.


第一个问题是你来自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;
Run Code Online (Sandbox Code Playgroud)

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

Java:                                  C++:

Player a = new Player();               Player *a = new Player();
Player b = a;                          Player *b = a;
b.foo();                               b->foo();
Run Code Online (Sandbox Code Playgroud)

在这段代码中有只有一个对象,你可以通过连接访问它ab它不会有所作为,a并且b都指向同一个对象.

C++:

Player c = Player();
Player d = c;
d.foo();
Run Code Online (Sandbox Code Playgroud)

在此代码中有两个对象.它们是截然不同的,做一些事情d并不影响c.

如果在Java中你了解了'原始'类型int和对象类型之间的区别,String那么考虑它的一种方法是在C++中所有对象都是原始的.如果我们回顾一下您的代码并使用"C++对象就像Java原语"规则,您可以更好地看到错误:

Java:
int[] players = new int[1];
players[0] = new int(playerWidth); // huh???
Run Code Online (Sandbox Code Playgroud)

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


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

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
Run Code Online (Sandbox Code Playgroud)

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

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

Player *p   = new Player;    // not an array
Player *arr = new Player[5]; // an array
Run Code Online (Sandbox Code Playgroud)

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

Player *x = p + 1;   // not pointing at a valid Player
Player *y = arr + 3; // pointing at the fourth array element
Run Code Online (Sandbox Code Playgroud)

new并且delete很难,如果你使用它们没有保护正确使用.为了证明这一点:

int *x = new int;
foo();
delete x;
Run Code Online (Sandbox Code Playgroud)

此代码容易出错,可能有误.具体来说,如果foo()抛出异常则x泄露.

在C++中,只要你有责任,比如当你打电话给newdelete时,你就有责任在以后打电话,你应该记住

RAII
责任*收购是初始化

*更频繁的人说'资源获取是初始化',但资源只是一种责任.我被说服在他的一个例外安全C++会谈中使用Jon Kalb的后一个术语.

RAII意味着无论何时您获得责任,它都应该看起来像是在初始化一个对象; 特别是你正在初始化一个特殊的对象,其目的是为你管理这个责任.这种类型的一个例子是std::unique_ptr<int>管理指向ints的指针new:

C++:

std::unique_ptr<int> x(new int);
foo();
// no 'delete x;'
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

std::vector<Player> players(1);
players[0] = Player(playerWidth, playerHeight, 20, 1);
Run Code Online (Sandbox Code Playgroud)

或者在C++ 11中:

std::vector<Player> players { Player(playerWidth, playerHeight, 20, 1) };
Run Code Online (Sandbox Code Playgroud)


Lil*_*ard 8

你的类型不匹配.这也就不足为奇了,你试图把它Player*存入一个已经分配好的Player!

Player* players = new Player[1];
Run Code Online (Sandbox Code Playgroud)

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

players[0] = new Player(...)
Run Code Online (Sandbox Code Playgroud)

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

players[0] = Player(...)
Run Code Online (Sandbox Code Playgroud)

或者,我猜你认为这更适合你,你应该new完全停止使用,然后使用std::vector.

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

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

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


Dar*_*rer 5

原因是,您的变量类型

players[0]
Run Code Online (Sandbox Code Playgroud)

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

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

Player* player = new Player(playerWidth, playerHeight, 20, 1);
Run Code Online (Sandbox Code Playgroud)

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

delete player;
Run Code Online (Sandbox Code Playgroud)

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

  • 值语义是 C++ 的关键部分。这个答案仅展示了如何在 C++ 编译器中编译类似 Java 的代码,并没有表明有更好和更简单的方法。我不是要求所有的 boost/C++0x,而是至少_提及_值语义。RAII 是奖励积分。 (2认同)