【问题标题】:Declaring multiple object pointers on one line causes compiler error在一行上声明多个对象指针会导致编译器错误
【发布时间】:2012-11-17 02:00:46
【问题描述】:

当我这样做时(在我的课堂上)

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;

一切正常。但是,如果我将声明更改为:

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

我得到这个编译器错误:

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

然后它说re_sprite_eyes 的候选对象是sf::Sprite 对象和/或引用。

为什么这不起作用?声明不一样吗?

【问题讨论】:

  • 您发现了 C/C++ 声明的一个有趣属性:星号属于变量,而不属于类型。不断提醒自己这一点的一种方法是在类型之后和星号之前放置空格。

标签: c++ oop sfml


【解决方案1】:

sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

不声明 3 个指针 - 它是一个指针和 2 个对象。

sf::Sprite* 不幸的是不适用于它之后声明的所有变量,仅适用于第一个。相当于

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

你想做的事:

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

您需要为每个变量打上一颗星。在这种情况下,我更愿意将星号保留在变量一侧,而不是类型,以明确这种情况。

【讨论】:

  • 请参阅 John Bode 的回答和我的解释。
  • 很好的解释,但我更喜欢多行格式。如果有点冗长,它更容易阅读。
【解决方案2】:

在 C 和 C++ 中,* 绑定到 声明符,而不是类型说明符。在这两种语言中,声明都是基于表达式的类型,而不是对象。

例如,假设您有一个指向名为pint 的指针,并且您想访问p 指向的int 值;您可以通过使用一元 * 运算符取消引用指针来实现,如下所示:

x = *p;

表达式的类型*pint;因此,p 的声明是

int *p;

无论您在同一声明语句中声明了多少个指针,这都是正确的;如果qr 也需要声明为指针,那么它们还需要将一元* 作为声明符的一部分:

int *p, *q, *r;

因为 表达式 *q*r 的类型为 int。可以写T *pT* pT * p 是C 和C++ 语法的一个意外;所有这些声明都将被解释为T (*p)

这就是为什么我不喜欢将指针和引用类型声明为 C++ 风格的原因

T* p;
T& r;

因为它暗示了对 C 和 C++ 声明语法如何工作的错误看法,从而导致您刚刚遇到的那种确切的困惑。但是,我已经编写了足够多的 C++ 来意识到有时这种风格确实使代码的意图更加清晰,尤其是在定义容器类型时。

但还是错了。


这是对轨道中的 Lightness Races(以及反对我将 T* p 惯例标记为“错误”的任何其他人)的回应(晚了两年)...

首先,您有一大堆问题就像这个,这些问题特别是由使用T* p 约定引起的,以及它是如何不使用的像人们期望的那样工作。这个网站上有多少问题的顺序是“为什么T* p, q 不将pq 声明为指针?”

引入混乱 - 本身应该足以阻止它的使用。

但除此之外,它不一致。不能将数组或函数与声明符分开,为什么要将指针与它分开?

“嗯,那是因为 []() 是后缀运算符,而 * 是一元的”。是的,是的,所以你为什么不将运算符与它的操作数关联起来?在T* p的声明中,T不是*的操作数,那我们为什么要写成这样呢?

如果a 是“指针数组”,我们为什么要写T* a[N]?如果f 是“返回指针的函数”,我们为什么要写T* f()?如果您将这些声明编写为T *a[N]T *f(),则声明系统更有意义并且内部一致。从我可以使用T 作为任何类型(实际上,对于任何声明说明符序列)的替代品这一事实来看,这一点应该很明显。

然后你有指向数组的指针和指向函数的指针,其中* 必须显式绑定到声明符1

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

是的,指针性是您要声明的事物的一个重要属性,但数组性和函数性也是如此,强调一个而不是另一个会产生比它解决的问题更多的问题。同样,正如这个问题所示,T* p 约定引入了混淆

因为* 是一元的,并且是一个单独的标记,所以您可以编写T* pT *pT*pT * p,它们都会被编译器接受,但它们都会被解释为T (*p)。更重要的是,T* p, q, r 将被解释为T (*p), q, r。如果你写T *p, q, r,这种解释会更明显。是的,是的,是的,“每行只声明一件事,这不会有问题。”你知道还有什么办法不让它成为问题吗? 正确地编写你的声明符。声明器系统本身会更有意义,并且您出错的可能性会更小。

我们不是在争论这种语言的“古老的古怪”,它是语言语法及其哲学的基本组成部分。指针属性是 declarator 的一个属性,就像数组属性和函数属性一样,并且假装它以某种方式 不是 只会导致混淆并使 C 和 C++ 都更难比他们需要的理解。

我认为将解引用运算符设为一元而不是后缀是一个错误2,但这就是它在 B 中的工作方式,Ritchie 希望尽可能多地保留 B。我还认为 Bjarne 推广T* p 约定是一个错误。


  1. 在讨论的这一点上,有人会建议使用 typedef 像
    typedef T arrtype[N]; 
    arrtype* p;
    这完全没有抓住重点,并在“C:完整参考”的第一版中赢得了建议者的殴打,因为它又大又重,对其他任何东西都没有好处。
  2. T (*(*a)[N])() 相比,写T a*[N]*() 绝对不会让人眼花缭乱,而且扫描起来也更容易。

【讨论】:

  • “T& r;”错了吗?不能写 T t = &r;我写“T *p;”和“T&r;”
  • 使用 & 来表示引用是一种 C++ 构造,与原始 C 声明不符。过去,我对引用进入 C++ 的方式持保留态度,部分原因是因为这个。 address-of 运算符以一种令人困惑的方式重载 - 不像 > 那样严重。 :-)
  • @JiveDadson:就语法而言,T&amp; r 是“错误的”(它被解释为T (&amp;r),因此必须编写多个声明T &amp;r, &amp;s, &amp;q)。我理解您的意思(&amp;x 的类型为 T *,而不是 T),是的,以这种方式重载 &amp; 确实会导致一些胃灼热。
  • 除了在这个特定的情况下(无论如何你都不应该这样做)(而且,好吧,如果你愿意的话,一些古老的复杂类型)它不可能是任何影响较小,因此 C++ 风格总体上是 far 优越的。这不是“错误的”。右对齐你的符号只是为了迎合语言内部的古老古怪是最大的抽象漏洞之一。
  • 我不同意“C++ 风格错误”的前提和“C++ 风格优越”的前提。 C++ 风格在某些情况下运行良好(例如,当每个声明只声明一个变量时)并在其他情况下误导程序员(例如,暗示Foo* a, b 声明了两个指针而不是一个)。另一种选择(基于技术和宗教的优点和缺点)是使用 typedef 或(自 C++ 起)类型别名,避免在变量/参数中争论 *&amp; 的位置声明,同时在更改类型时简化代码维护。
【解决方案3】:

在 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;};

【讨论】:

    【解决方案4】:

    另一件可能引起你注意的事情是这行:

    int * p1, * p2;
    

    这声明了前面示例中使用的两个指针。但请注意,每个指针都有一个星号 (*),以便两者都具有 int* 类型(指向 int 的指针)。由于优先规则,这是必需的。请注意,如果代码是:

    int * p1, p2;
    

    p1 确实属于int* 类型,但p2 将属于int 类型。为此目的,空间根本不重要。但无论如何,对于大多数有兴趣在每个语句中声明多个指针的指针用户来说,只需记住每个指针放一个星号就足够了。甚至更好:为每个变量使用不同的 statemet。

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

    【讨论】:

      【解决方案5】:

      星号绑定到指针变量名称。记住这一点的方法是注意在 C/C++ 中,声明模仿用法。

      指针可以这样使用:

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

      同样,

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

      在这两种情况下,都有一个基础类型说明符,以及在表达式中“获取”该类型对象所需的一个或多个运算符。

      【讨论】:

        猜你喜欢
        • 2021-05-08
        • 1970-01-01
        • 2014-08-09
        • 2016-05-13
        • 2017-07-12
        • 2019-01-30
        • 2016-11-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多