26

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

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和/或引用。

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

4

5 回答 5

63

sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

不声明 3 个指针 - 它是 1 个指针和 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;

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

于 2012-11-29T03:20:36.533 回答
26

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

例如,假设您有一个指向intnamed的指针p,并且您想要访问指向的 intp;您可以通过使用一元运算符取消引用指针来做到这一点*,如下所示:

x = *p;

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

int *p;

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

int *p, *q, *r;

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

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

T* p;
T& r;

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

但这仍然是错误的。


T* p这是对轨道上的轻量级竞赛(以及反对我将公约标记为“错误”的任何其他人)的(迟了两年)的回应......

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

引入了混乱——这本身就足以阻止它的使用。

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

“嗯,那是因为[]and()是后缀运算符,而 while*是一元的”。是的,是的,那么为什么不将运算符与其操作数相关联呢?在声明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* p, T *p, T*p,T * p并且它们都将被编译器接受,但它们都将被解释为T (*p). 更重要的是,T* p, q, r将被解释为T (*p), q, r. 如果你写的话,这种解释会更明显T *p, q, r。是的,是的,是的,“每行只声明一件事,这不会有问题。” 你知道还有什么办法不让它成为问题吗? 正确地编写你的声明器。声明器系统本身将更有意义,您将不太可能犯错误。

我们不是在争论这种语言的“古老古怪”,它是语言语法及其哲学的基本组成部分。Pointer-ness 是declarator的一个属性,就像 array-ness 和 function-ness 一样,假装它不仅会导致混淆,而且会使 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])()绝对不那么刺眼,并且更容易扫描。
于 2012-11-29T03:59:11.780 回答
5

在 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;};
于 2012-11-29T04:52:36.543 回答
3

可能引起您注意的另一件事是以下行:

int * p1, * p2;

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

int * p1, p2;

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

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

于 2015-03-25T05:49:50.057 回答
1

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

指针可以这样使用:

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

相似地,

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

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

于 2012-11-29T04:14:20.800 回答