简体   繁体   English

在一行上声明多个对象指针会导致编译器错误

[英]Declaring multiple object pointers on one line causes compiler error

when I do this (in my class)当我这样做时(在我的课堂上)

public:
    Entity()
    {
        re_sprite_eyes = new sf::Sprite();
        re_sprite_hair = new sf::Sprite();
        re_sprite_body = new sf::Sprite();
    }

private:
    sf::Sprite* re_sprite_hair;
    sf::Sprite* re_sprite_body;
    sf::Sprite* re_sprite_eyes;

Everything works fine.一切正常。 However, if I change the declarations to this:但是,如果我将声明更改为:

private:
    sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

I get this compiler error:我收到此编译器错误:

error: no match for 'operator=' in '((Entity*)this)->Entity::re_sprite_eyes = (operator new(272u), (<statement>, ((sf::Sprite*)<anonymous>)))

And then it says candidates for re_sprite_eyes are sf::Sprite objects and/or references.然后它说re_sprite_eyes候选对象是sf::Sprite对象和/或引用。

Why does this not work?为什么这不起作用? Aren't the declarations the same?声明不一样吗?

sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

Does not declare 3 pointers - it is one pointer and 2 objects.不声明 3 个指针 - 它是一个指针和 2 个对象。

sf::Sprite* unfortunately does not apply to all the variables declared following it, just the first.不幸的是, sf::Sprite*不适用于其后声明的所有变量,仅适用于第一个。 It is equivalent to它相当于

sf::Sprite* re_sprite_hair;
sf::Sprite re_sprite_body;
sf::Sprite re_sprite_eyes;

You want to do:你想做:

sf::Sprite *re_sprite_hair, *re_sprite_body, *re_sprite_eyes;

You need to put one star for each variable.您需要为每个变量打一颗星。 In such cases I prefer to keep the star on the variable's side, rather than the type, to make exactly this situation clear.在这种情况下,我更喜欢将星号保留在变量一侧,而不是类型,以明确这种情况。

In both C and C++, the * binds to the declarator , not the type specifier.在 C 和 C++ 中, *绑定到declarator ,而不是类型说明符。 In both languages, declarations are based on the types of expressions , not objects.在这两种语言中,声明都是基于表达式的类型,而不是对象。

For example, suppose you have a pointer to an int named p , and you want to access the int value that p points to;例如,假设您有一个指向名为pint的指针,并且您想访问p指向的int值; you do so by dereferencing the pointer with the unary * operator, like so:您可以通过使用一元*运算符取消引用指针来实现,如下所示:

x = *p;

The type of the expression *p is int ;表达式*p的类型是int thus, the declaration of p is因此, p的声明是

int *p;

This is true no matter how many pointers you declare within the same declaration statement;无论您在同一声明语句中声明多少个指针,这都是正确的; if q and r also need to be declared as pointers, then they also need to have the unary * as part of the declarator:如果qr也需要声明为指针,那么它们还需要将一元*作为声明符的一部分:

int *p, *q, *r;

because the expressions *q and *r have type int .因为表达式*q*r类型为int It's an accident of C and C++ syntax that you can write T *p , T* p , or T * p ;您可以编写T *pT* pT * p是 C 和 C++ 语法的一个意外; all of those declarations will be interpreted as T (*p) .所有这些声明都将被解释为T (*p)

This is why I'm not fond of the C++ style of declaring pointer and reference types as这就是为什么我不喜欢将指针和引用类型声明为 C++ 风格的原因

T* p;
T& r;

because it implies an incorrect view of how C and C++ declaration syntax works, leading to the exact kind of confusion that you just experienced.因为它暗示了对 C 和 C++ 声明语法如何工作的不正确看法,导致您刚刚经历的那种确切的混乱。 However, I've written enough C++ to realize that there are times when that style does make the intent of the code clearer, especially when defining container types.但是,我已经编写了足够多的 C++ 以意识到有时这种风格确实使代码的意图更加清晰,尤其是在定义容器类型时。

But it's still wrong.但它仍然是错误的。


This is a (two years late) response to Lightness Races in Orbit (and anyone else who objects to my labeling the T* p convention as "wrong")...这是(晚了两年)对轨道上的 Lightness Races 的回应(以及任何其他反对我将T* p约定标记为“错误”的人)......

First of all, you have the legion of questions just like this one that arise specifically from the use of the T* p convention, and how it doesn't work like people expect.首先,您有大量的问题,就像这个问题一样,这些问题专门由使用T* p约定而产生,以及它如何不像人们期望的那样工作。 How many questions on this site are on the order of "why doesn't T* p, q declare both p and q as pointers?"这个站点上有多少问题是按照“为什么T* p, qpq都声明为指针?”的顺序排列的?

It introduces confusion - that by itself should be enough to discourage its use.引入了混乱——这本身就足以阻止它的使用。

But beyond that, it's inconsistent .但除此之外,它是不一致的 You can't separate array-ness or function-ness from the declarator, why should you separate pointer-ness from it?你不能从声明符中分离数组性或函数性,为什么要从中分离指针性?

"Well, that's because [] and () are postfix operators, while * is unary". “嗯,那是因为[]()是后缀运算符,而*是一元的”。 Yes, it is, so why aren't you associating the operator with its operand ?是的,是的,那么为什么不将运算符与其操作数相关联呢? In the declaration T* p , T is not the operand of * , so why are we writing the declaration as though it is?在声明T* pT不是*的操作数,那么为什么我们要写声明呢?

If a is "an array of pointers", why should we write T* a[N] ?如果a是“指针数组”,我们为什么要写T* a[N] If f is "a function returning a pointer", why should we write T* f() ?如果f是“一个返回指针的函数”,我们为什么要写T* f() The declarator system makes more sense and is internally consistent if you write those declarations as T *a[N] and T *f() .如果将这些声明编写为T *a[N]T *f() ,则声明器系统更有意义并且内部一致 That should be obvious from the fact that I can use T as a stand-in for any type (indeed, for any sequence of declaration specifiers).这应该是显而易见的,因为我可以使用T作为任何类型的替代品(实际上,对于任何声明说明符序列)。

And then you have pointers to arrays and pointers to functions, where the * must be explicitly bound to the declarator 1 :然后你有指向数组的指针和指向函数的指针,其中*必须显式绑定到声明符1

T (*a)[N];
T (*f)();

Yes, pointer-ness is an important property of the thing you're declaring, but so are array-ness and function-ness, and emphasizing one over the other creates more problems than it solves.是的,指针性是您声明的事物的一个重要属性,但数组性和函数性也是如此,强调一个而不是另一个会产生更多的问题,而不是它解决的问题。 Again, as this question shows, the T* p convention introduces confusion .同样,正如这个问题所示, T* p约定引入了混淆

Because * is unary and a separate token on its own you can write T* p , T *p , T*p , and T * p and they'll all be accepted by the compiler, but they will all be interpreted as T (*p) .因为*是一元的,并且它本身是一个单独的标记,所以您可以编写T* pT *pT*pT * p并且它们都会被编译器接受,但它们都会被解释为T (*p) More importantly, T* p, q, r will be interpreted as T (*p), q, r .更重要的是, T* p, q, r将被解释为T (*p), q, r That interpretation is more obvious if you write T *p, q, r .如果你写T *p, q, r这种解释会更明显。 Yeah, yeah, yeah, "declare only one thing per line and it won't be a problem."是的,是的,是的,“每行只声明一件事,这不会有问题。” You know how else to not make it a problem?你知道如何不让它成为问题吗? Write your declarators properly .正确编写您的声明符 The declarator system itself will make more sense and you will be less likely to make mistake.声明器系统本身会更有意义,而且你犯错的可能性也会降低。

We're not arguing over an "antique oddity" of the language, it's a fundamental component of the language grammar and its philosophy.我们不是在争论语言的“古怪”,它是语言语法及其哲学的基本组成部分。 Pointer-ness is a property of the declarator , just like array-ness and function-ness, and pretending it's somehow not just leads to confusion and makes both C and C++ harder to understand than they need to be.指针岬是说明符的属性,就像数组的烦躁和功能的烦躁,并且假装它在某种程度上只是导致混乱,使C和C ++更难理解比他们需要。

I would argue that making the dereference operator unary as opposed to postfix was a mistake 2 , but that's how it worked in B, and Ritchie wanted to keep as much of B as possible.我会争辩说,使解引用运算符一元而不是后缀是一个错误2 ,但这就是它在 B 中的工作方式,而 Ritchie 想保留尽可能多的 B 。 I will also argue that Bjarne's promotion of the T* p convention is a mistake.我还将争辩说 Bjarne 对T* p约定的推广是一个错误。


  1. At this point in the discussion, somebody will suggest using a typedef like在讨论的这一点上,有人会建议使用像这样的 typedef
     typedef T arrtype[N]; arrtype* p;
    which just totally misses the point and earns the suggester a beating with the first edition of "C: The Complete Reference" because it's big and heavy and no good for anything else.这完全没有抓住要点,并且在第一版“C:完整参考”中赢得了建议者的打击,因为它又大又重,对其他任何东西都没有好处。
  2. Writing T a*[N]*() as opposed to T (*(*a)[N])() is definitely less eye-stabby and scans much more easily.T (*(*a)[N])()相比,编写T a*[N]*()绝对不那么刺眼并且更容易扫描。

In C++11 you have a nice little workaround, which might be better than shifting spaces back and forth:在 C++11 中,您有一个不错的小解决方法,这可能比来回移动空格更好:

template<typename T> using type=T;
template<typename T> using func=T*;

// I don't like this style, but type<int*> i, j; works ok
type<int*> i = new int{3},
           j = new int{4};

// But this one, imho, is much more readable than int(*f)(int, int) = ...
func<int(int, int)> f = [](int x, int y){return x + y;},
                    g = [](int x, int y){return x - y;};

Another thing that may call your attention is the line:另一件可能引起您注意的事情是:

 int * p1, * p2;

This declares the two pointers used in the previous example.这声明了前一个示例中使用的两个指针。 But notice that there is an asterisk ( * ) for each pointer, in order for both to have type int* (pointer to int).但请注意,每个指针都有一个星号 ( * ),以便两者都具有int*类型(指向 int 的指针)。 This is required due to the precedence rules.由于优先规则,这是必需的。 Note that if, instead, the code was:请注意,如果代码是:

 int * p1, p2;

p1 would indeed be of type int* , but p2 would be of type int . p1确实是int*类型,但p2int类型。 Spaces do not matter at all for this purpose.为此,空格根本无关紧要。 But anyway, simply remembering to put one asterisk per pointer is enough for most pointer users interested in declaring multiple pointers per statement.但无论如何,对于大多数对每个语句声明多个指针感兴趣的指针用户来说,只需记住为每个指针放置一个星号就足够了。 Or even better: use a different statemet for each variable.或者甚至更好:为每个变量使用不同的 statemet。

From http://www.cplusplus.com/doc/tutorial/pointers/来自http://www.cplusplus.com/doc/tutorial/pointers/

The asterisk binds to the pointer-variable name.星号绑定到指针变量名称。 The way to remember this is to notice that in C/C++, declarations mimic usage.记住这一点的方法是注意在 C/C++ 中,声明模仿用法。

The pointers might be used like this:指针可以这样使用:

sf::Sprite *re_sprite_body;
// ...
sf::Sprite sprite_bod = *re_sprite_body;

Similarly,相似地,

char *foo[3];
// ...
char fooch = *foo[1];

In both cases, there is an underlying type-specifier, and the operator or operators required to "get to" an object of that type in an expression.在这两种情况下,都有一个底层类型说明符,以及在表达式中“到达”该类型对象所需的一个或多个运算符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM